]> Pileus Git - ~andy/fetchmail/blobdiff - uid.c
SECURITY FIX (one missed): DoS on EILSEQ in report_*() in -vv and multibyte-locales.
[~andy/fetchmail] / uid.c
diff --git a/uid.c b/uid.c
index ff993f59cbeb0d17fd829402c0fc8ea8cc741c63..9a62ee249a15caf8d8f4bd22214b66fb03eca221 100644 (file)
--- a/uid.c
+++ b/uid.c
@@ -1,5 +1,5 @@
-/*
- * uid.c -- UIDL handling for POP3 servers without LAST
+/**
+ * \file uid.c -- UIDL handling for POP3 servers without LAST
  *
  * For license terms, see the file COPYING in this directory.
  */
@@ -20,6 +20,7 @@
 
 #include "fetchmail.h"
 #include "i18n.h"
+#include "sdump.h"
 
 /*
  * Machinery for handling UID lists live here.  This is mainly to support
@@ -177,6 +178,9 @@ void initialize_saved_lists(struct query *hostlist, const char *idfile)
             * the lotus notes case.
             * So we start looking for the '@' after which the
             * host will follow with the ' ' seperator finaly id.
+            *
+            * XXX FIXME: There is a case this code cannot handle:
+            * the user name cannot have blanks after a '@'.
             */
            if ((delimp1 = strchr(user, '@')) != NULL &&
                (id = strchr(delimp1,' ')) != NULL)
@@ -193,44 +197,43 @@ void initialize_saved_lists(struct query *hostlist, const char *idfile)
                id = id + strspn(id, " ");
 
                delimp1++; /* but what if there is only white space ?!? */
-               saveddelim1 = *delimp1; /* save char after token */
+               /* we have at least one @, else we are not in this branch */
+               saveddelim1 = *delimp1;         /* save char after token */
                *delimp1 = '\0';                /* delimit token with \0 */
-               if (id != NULL) 
-               {
-                   /* now remove trailing white space chars from id */
-                   if ((delimp2 = strpbrk(id, " \t\n")) != NULL ) {
-                       saveddelim2 = *delimp2;
-                       *delimp2 = '\0';
-                   }
-                   atsign = strrchr(user, '@');
-                   if (atsign) {
-                       *atsign = '\0';
-                       host = atsign + 1;
 
-                   }
-                   for (ctl = hostlist; ctl; ctl = ctl->next) {
-                       if (strcasecmp(host, ctl->server.queryname) == 0
+               /* now remove trailing white space chars from id */
+               if ((delimp2 = strpbrk(id, " \t\n")) != NULL ) {
+                   saveddelim2 = *delimp2;
+                   *delimp2 = '\0';
+               }
+
+               atsign = strrchr(user, '@');
+               /* we have at least one @, else we are not in this branch */
+               *atsign = '\0';
+               host = atsign + 1;
+
+               /* find proper list and save it */
+               for (ctl = hostlist; ctl; ctl = ctl->next) {
+                   if (strcasecmp(host, ctl->server.queryname) == 0
                            && strcasecmp(user, ctl->remotename) == 0) {
-       
-                           save_str(&ctl->oldsaved, id, UID_SEEN);
-                           break;
-                       }
+                       save_str(&ctl->oldsaved, id, UID_SEEN);
+                       break;
                    }
-                   /* 
-                    * If it's not in a host we're querying,
-                    * save it anyway.  Otherwise we'd lose UIDL
-                    * information any time we queried an explicit
-                    * subset of hosts.
-                    */
-                   if (ctl == (struct query *)NULL) {
-                               /* restore string */
-                       *delimp1 = saveddelim1;
-                       *atsign = '@';
-                       if (delimp2 != NULL) {
-                           *delimp2 = saveddelim2;
-                       }
-                       save_str(&scratchlist, buf, UID_SEEN);
+               }
+               /* 
+                * If it's not in a host we're querying,
+                * save it anyway.  Otherwise we'd lose UIDL
+                * information any time we queried an explicit
+                * subset of hosts.
+                */
+               if (ctl == (struct query *)NULL) {
+                   /* restore string */
+                   *delimp1 = saveddelim1;
+                   *atsign = '@';
+                   if (delimp2 != NULL) {
+                       *delimp2 = saveddelim2;
                    }
+                   save_str(&scratchlist, buf, UID_SEEN);
                }
            }
        }
@@ -247,8 +250,11 @@ void initialize_saved_lists(struct query *hostlist, const char *idfile)
            {
                report_build(stdout, GT_("Old UID list from %s:"), 
                             ctl->server.pollname);
-               for (idp = ctl->oldsaved; idp; idp = idp->next)
-                   report_build(stdout, " %s", (char *)idp->id);
+               for (idp = ctl->oldsaved; idp; idp = idp->next) {
+                   char *t = sdump(idp->id, strlen(idp->id));
+                   report_build(stdout, " %s", t);
+                   free(t);
+               }
                if (!idp)
                    report_build(stdout, GT_(" <empty>"));
                report_complete(stdout, "\n");
@@ -258,8 +264,11 @@ void initialize_saved_lists(struct query *hostlist, const char *idfile)
        if (uidlcount)
        {
            report_build(stdout, GT_("Scratch list of UIDs:"));
-           for (idp = scratchlist; idp; idp = idp->next)
-               report_build(stdout, " %s", (char *)idp->id);
+           for (idp = scratchlist; idp; idp = idp->next) {
+               char *t = sdump(idp->id, strlen(idp->id));
+               report_build(stdout, " %s", t);
+               free(t);
+           }
            if (!idp)
                report_build(stdout, GT_(" <empty>"));
            report_complete(stdout, "\n");
@@ -282,7 +291,7 @@ void initialize_saved_lists(struct query *hostlist, const char *idfile)
        continue;
 
     *end = (struct idlist *)xmalloc(sizeof(struct idlist));
-    (*end)->id = (unsigned char *)str;
+    (*end)->id = str;
     (*end)->val.status.mark = status;
     (*end)->val.status.num = 0;
     (*end)->next = NULL;
@@ -349,25 +358,26 @@ struct idlist *str_in_list(struct idlist **idl, const char *str, const flag case
     struct idlist *walk;
     if (caseblind) {
        for( walk = *idl; walk; walk = walk->next )
-           if( strcasecmp( str, (char *)walk->id) == 0 )
+           if( strcasecmp( str, walk->id) == 0 )
                return walk;
     } else {
        for( walk = *idl; walk; walk = walk->next )
-           if( strcmp( str, (char *)walk->id) == 0 )
+           if( strcmp( str, walk->id) == 0 )
                return walk;
     }
     return NULL;
 }
 
-int str_nr_in_list( struct idlist **idl, const char *str )
-  /* return the position of str in idl */
+/** return the position of first occurrence of \a str in \a idl */
+int str_nr_in_list(struct idlist **idl, const char *str)
 {
     int nr;
     struct idlist *walk;
-    if ( !str )
+
+    if (!str)
         return -1;
-    for( walk = *idl, nr = 0; walk; nr ++, walk = walk->next )
-        if( strcmp( str, walk->id) == 0 )
+    for (walk = *idl, nr = 0; walk; nr ++, walk = walk->next)
+        if (strcmp(str, walk->id) == 0)
            return nr;
     return -1;
 }
@@ -514,8 +524,11 @@ void uid_swap_lists(struct query *ctl)
            report_build(stdout, GT_("Merged UID list from %s:"), ctl->server.pollname);
        else
            report_build(stdout, GT_("New UID list from %s:"), ctl->server.pollname);
-       for (idp = dofastuidl ? ctl->oldsaved : ctl->newsaved; idp; idp = idp->next)
-           report_build(stdout, " %s = %d", (char *)idp->id, idp->val.status.mark);
+       for (idp = dofastuidl ? ctl->oldsaved : ctl->newsaved; idp; idp = idp->next) {
+           char *t = sdump(idp->id, strlen(idp->id));
+           report_build(stdout, " %s = %d", t, idp->val.status.mark);
+           free(t);
+        }
        if (!idp)
            report_build(stdout, GT_(" <empty>"));
        report_complete(stdout, "\n");
@@ -564,8 +577,11 @@ void uid_discard_new_list(struct query *ctl)
        /* this is now a merged list! the mails which were seen in this
         * poll are marked here. */
        report_build(stdout, GT_("Merged UID list from %s:"), ctl->server.pollname);
-       for (idp = ctl->oldsaved; idp; idp = idp->next)
-           report_build(stdout, " %s = %d", (char *)idp->id, idp->val.status.mark);
+       for (idp = ctl->oldsaved; idp; idp = idp->next) {
+           char *t = sdump(idp->id, strlen(idp->id));
+           report_build(stdout, " %s = %d", t, idp->val.status.mark);
+           free(t);
+       }
        if (!idp)
            report_build(stdout, GT_(" <empty>"));
        report_complete(stdout, "\n");
@@ -609,12 +625,14 @@ void write_saved_lists(struct query *hostlist, const char *idfile)
     /* either nuke the file or write updated last-seen IDs */
     if (!idcount && !scratchlist)
     {
-       if (outlevel >= O_DEBUG)
-           report(stdout, GT_("Deleting fetchids file.\n"));
+       if (outlevel >= O_DEBUG) {
+           if (access(idfile, F_OK) == 0)
+                   report(stdout, GT_("Deleting fetchids file.\n"));
+       }
        if (unlink(idfile) && errno != ENOENT)
            report(stderr, GT_("Error deleting %s: %s\n"), idfile, strerror(errno));
     } else {
-       char *newnam = xmalloc(strlen(idfile) + 2);
+       char *newnam = (char *)xmalloc(strlen(idfile) + 2);
        strcpy(newnam, idfile);
        strcat(newnam, "_");
        if (outlevel >= O_DEBUG)
@@ -627,7 +645,7 @@ void write_saved_lists(struct query *hostlist, const char *idfile)
                    if (idp->val.status.mark == UID_SEEN
                                || idp->val.status.mark == UID_DELETED)
                        fprintf(tmpfp, "%s@%s %s\n", 
-                           ctl->remotename, ctl->server.queryname, (char *)idp->id);
+                           ctl->remotename, ctl->server.queryname, idp->id);
            }
            for (idp = scratchlist; idp; idp = idp->next)
                fputs(idp->id, tmpfp);