* Renamed "struct user" to "struct ctdluser"
[citadel.git] / citadel / citadel_ipc.c
index 88a3b5a91af168bb82ae403e123e5dd22021c494..f69d4be05f751864ab304068265daa89f0a86f2a 100644 (file)
@@ -1,7 +1,11 @@
 /* $Id$ */
 
 #define        UDS                     "_UDS_"
+#ifdef __CYGWIN__
+#define DEFAULT_HOST           "localhost"
+#else
 #define DEFAULT_HOST           UDS
+#endif
 #define DEFAULT_PORT           "citadel"
 
 #include "sysdep.h"
@@ -19,7 +23,9 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <string.h>
+#ifdef HAVE_MALLOC_H
 #include <malloc.h>
+#endif
 #include <stdlib.h>
 #include <ctype.h>
 #include <sys/socket.h>
@@ -60,15 +66,19 @@ void setCryptoStatusHook(void (*hook)(char *s)) {
        status_hook = hook;
 }
 
+void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
+       ipc->network_status_cb = hook;
+}
+
 
 char express_msgs = 0;
 
 
-static void serv_read(CtdlIPC *ipc, char *buf, int bytes);
-static void serv_write(CtdlIPC *ipc, const char *buf, int nbytes);
+static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
+static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
 #ifdef HAVE_OPENSSL
-static void serv_read_ssl(CtdlIPC *ipc, char *buf, int bytes);
-static void serv_write_ssl(CtdlIPC *ipc, const char *buf, int nbytes);
+static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
+static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
 static void ssl_lock(int mode, int n, const char *file, int line);
 static void endtls(SSL *ssl);
 #ifdef THREADED_CLIENT
@@ -191,6 +201,28 @@ int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
 }
 
 
+/*
+ * Second stage of authentication - provide password.  The server returns
+ * 200 and several arguments in cret relating to the user's account.
+ */
+int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!response) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(response) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "PAS2 %s", response);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
 /*
  * Create a new user.  This returns 200 plus the same arguments as TryPassword
  * if selfservice is nonzero, unless there was a problem creating the account.
@@ -239,13 +271,14 @@ int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
 
 /* LKRN */
 /* Caller must free the march list */
-/* which is 0 = LRMS, 1 = LKRN, 2 = LKRO, 3 = LKRA, 4 = LZRM */
+/* Room types are defined in enum RoomList; keep these in sync! */
 /* floor is -1 for all, or floornum */
-int CtdlIPCKnownRooms(CtdlIPC *ipc, int which, int floor, struct march **listing, char *cret)
+int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
 {
        register int ret;
        struct march *march = NULL;
-       static char *proto[] = {"LRMS", "LKRN", "LKRO", "LKRA", "LZRM" };
+       static char *proto[] =
+               {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
        char aaa[SIZ];
        char *bbb = NULL;
        size_t bbbsize;
@@ -253,7 +286,7 @@ int CtdlIPCKnownRooms(CtdlIPC *ipc, int which, int floor, struct march **listing
        if (!listing) return -2;
        if (*listing) return -2;        /* Free the listing first */
        if (!cret) return -2;
-       if (which < 0 || which > 4) return -2;
+       /* if (which < 0 || which > 4) return -2; */
        if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
 
        sprintf(aaa, "%s %d", proto[which], floor);
@@ -271,8 +304,11 @@ int CtdlIPCKnownRooms(CtdlIPC *ipc, int which, int floor, struct march **listing
                        if (mptr) {
                                mptr->next = NULL;
                                extract(mptr->march_name, aaa, 0);
+                               mptr->march_flags = (unsigned int) extract_int(aaa, 1);
                                mptr->march_floor = (char) extract_int(aaa, 2);
                                mptr->march_order = (char) extract_int(aaa, 3);
+                               mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
+                               mptr->march_access = (char) extract_int(aaa, 5);
                                if (march == NULL)
                                        march = mptr;
                                else {
@@ -292,14 +328,14 @@ int CtdlIPCKnownRooms(CtdlIPC *ipc, int which, int floor, struct march **listing
 
 
 /* GETU */
-/* Caller must free the struct usersupp; caller may pass an existing one */
-int CtdlIPCGetConfig(CtdlIPC *ipc, struct usersupp **uret, char *cret)
+/* Caller must free the struct ctdluser; caller may pass an existing one */
+int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
 {
        register int ret;
 
        if (!cret) return -2;
        if (!uret) return -2;
-       if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
+       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
        if (!*uret) return -1;
 
        ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
@@ -313,7 +349,7 @@ int CtdlIPCGetConfig(CtdlIPC *ipc, struct usersupp **uret, char *cret)
 
 
 /* SETU */
-int CtdlIPCSetConfig(CtdlIPC *ipc, struct usersupp *uret, char *cret)
+int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
 {
        char aaa[48];
 
@@ -377,15 +413,15 @@ int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
 /* MSGS */
 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
 /* whicharg is number of messages, applies to last, first, gt, lt */
-int CtdlIPCGetMessages(CtdlIPC *ipc, int which, int whicharg, const char *template,
-               long **mret, char *cret)
+int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
+               const char *mtemplate, unsigned long **mret, char *cret)
 {
        register int ret;
-       register long count = 0;
+       register unsigned long count = 0;
        static char *proto[] =
                { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
        char aaa[33];
-       char *bbb;
+       char *bbb = NULL;
        size_t bbbsize;
 
        if (!cret) return -2;
@@ -395,25 +431,31 @@ int CtdlIPCGetMessages(CtdlIPC *ipc, int which, int whicharg, const char *templa
 
        if (which <= 2)
                sprintf(aaa, "MSGS %s||%d", proto[which],
-                               (template) ? 1 : 0);
+                               (mtemplate) ? 1 : 0);
        else
                sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
-                               (template) ? 1 : 0);
-       if (template) count = strlen(template);
-       ret = CtdlIPCGenericCommand(ipc, aaa, template, count, &bbb, &bbbsize, cret);
+                               (mtemplate) ? 1 : 0);
+       if (mtemplate) count = strlen(mtemplate);
+       ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbbsize, cret);
+       if (ret / 100 != 1)
+               return ret;
        count = 0;
-       while (strlen(bbb)) {
-               int a;
-
+       *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
+       if (!*mret)
+               return -1;
+       while (bbb && strlen(bbb)) {
                extract_token(aaa, bbb, 0, '\n');
-               a = strlen(aaa);
-               memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
-               *mret = (long *)realloc(mret,
-                                       (size_t)((count + 1) * sizeof (long)));
-               if (*mret)
-                       *mret[count++] = atol(aaa);
-               *mret[count] = 0L;
+               remove_token(bbb, 0, '\n');
+               *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
+                                       sizeof (unsigned long)));
+               if (*mret) {
+                       (*mret)[count++] = atol(aaa);
+                       (*mret)[count] = 0L;
+               } else {
+                       break;
+               }
        }
+       if (bbb) free(bbb);
        return ret;
 }
 
@@ -536,12 +578,13 @@ int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
                                        }
                                        remove_token(bbb, 0, '\n');
                                } while ((bbb[0] != 0) && (bbb[0] != '\n'));
+                               remove_token(bbb, 0, '\n');
                        }
 
 
                }
                if (strlen(bbb)) {
-                       /* Strip trailing whitespace */
+                       /* FIXME: Strip trailing whitespace */
                        bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
                } else {
                        bbb = (char *)realloc(bbb, 1);
@@ -644,10 +687,12 @@ int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
 
        if (!cret) return -2;
 
-       if (msgnum)
+       if (msgnum) {
                sprintf(aaa, "SLRP %ld", msgnum);
-       else
+       }
+       else {
                sprintf(aaa, "SLRP HIGHEST");
+       }
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
        return ret;
 }
@@ -691,13 +736,13 @@ int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
 
 
 /* GETR */
-int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
+int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
 {
        register int ret;
 
        if (!cret) return -2;
        if (!qret) return -2;
-       if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
+       if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
        if (!*qret) return -1;
 
        ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
@@ -708,6 +753,8 @@ int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
                qret[0]->QRflags = extract_int(cret, 3);
                qret[0]->QRfloor = extract_int(cret, 4);
                qret[0]->QRorder = extract_int(cret, 5);
+               qret[0]->QRdefaultview = extract_int(cret, 6);
+               qret[0]->QRflags2 = extract_int(cret, 7);
        }
        return ret;
 }
@@ -715,7 +762,7 @@ int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
 
 /* SETR */
 /* set forget to kick all users out of room */
-int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct quickroom *qret, char *cret)
+int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
 {
        register int ret;
        char *aaa;
@@ -768,19 +815,16 @@ int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
 {
        register int ret;
-       char *aaa;
+       char cmd[SIZ];
 
        if (!cret) return -2;
        if (!mr) return -2;
 
-       aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
-                       mr->type, mr->author);
-       ret = CtdlIPCGenericCommand(ipc, aaa, mr->text, strlen(mr->text), NULL,
+       snprintf(cmd, sizeof cmd,
+                       "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
+                       mr->anonymous, mr->type, mr->subject, mr->author);
+       ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
                        NULL, cret);
-       free(aaa);
        return ret;
 }
 
@@ -1091,7 +1135,9 @@ int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
 
 /* OPEN */
 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               void (*progress_gauge_callback)(long, long), char *cret)
+               size_t resume,
+               void (*progress_gauge_callback)(unsigned long, unsigned long),
+               char *cret)
 {
        register int ret;
        size_t bytes;
@@ -1116,11 +1162,11 @@ int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
                bytes = extract_long(cret, 0);
                last_mod = extract_int(cret, 1);
                extract(mimetype, cret, 2);
-               ret = CtdlIPCReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
-/*             ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, progress_gauge_callback, cret); */
+/*             ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
                ret = CtdlIPCEndDownload(ipc, cret);
                if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
                                        filename, mimetype);
        }
        return ret;
@@ -1128,15 +1174,17 @@ int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
 
 
 /* OPNA */
-int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part, void **buf,
-               void (*progress_gauge_callback)(long, long), char *cret)
+int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
+               void **buf,
+               void (*progress_gauge_callback)(unsigned long, unsigned long),
+               char *cret)
 {
        register int ret;
        size_t bytes;
        time_t last_mod;
        char filename[SIZ];
        char mimetype[SIZ];
-       char *aaa;
+       char aaa[SIZ];
 
        if (!cret) return -2;
        if (!buf) return -2;
@@ -1145,21 +1193,19 @@ int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part, void
        if (!msgnum) return -2;
        if (ipc->downloading) return -2;
 
-       aaa = (char *)malloc(strlen(part) + 17);
-       if (!aaa) return -1;
-
        sprintf(aaa, "OPNA %ld|%s", msgnum, part);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
        if (ret / 100 == 2) {
                ipc->downloading = 1;
                bytes = extract_long(cret, 0);
                last_mod = extract_int(cret, 1);
-               extract(mimetype, cret, 2);
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
+               extract(filename, cret, 2);
+               extract(mimetype, cret, 3);
+               /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
                ret = CtdlIPCEndDownload(ipc, cret);
                if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
                                        filename, mimetype);
        }
        return ret;
@@ -1168,7 +1214,8 @@ int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part, void
 
 /* OIMG */
 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               void (*progress_gauge_callback)(long, long), char *cret)
+               void (*progress_gauge_callback)(unsigned long, unsigned long),
+               char *cret)
 {
        register int ret;
        size_t bytes;
@@ -1193,10 +1240,11 @@ int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
                bytes = extract_long(cret, 0);
                last_mod = extract_int(cret, 1);
                extract(mimetype, cret, 2);
-               ret = CtdlIPCReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
+/*             ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
                ret = CtdlIPCEndDownload(ipc, cret);
                if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
                                        filename, mimetype);
        }
        return ret;
@@ -1205,7 +1253,8 @@ int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
 
 /* UOPN */
 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
-               const char *path, void (*progress_gauge_callback)(long, long),
+               const char *path,
+               void (*progress_gauge_callback)(unsigned long, unsigned long),
                char *cret)
 {
        register int ret;
@@ -1237,7 +1286,8 @@ int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
 /* UIMG */
 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
                const char *save_as,
-               void (*progress_gauge_callback)(long, long), char *cret)
+               void (*progress_gauge_callback)(unsigned long, unsigned long),
+               char *cret)
 {
        register int ret;
        char *aaa;
@@ -1300,17 +1350,13 @@ int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
 {
        register int ret;
-       char *aaa;
+       char aaa[SIZ];
 
        if (!cret) return -2;
        if (!name) return -2;
 
-       aaa = (char *)malloc(strlen(name) + 17);
-       if (!aaa) return -1;
-
        sprintf(aaa, "CFLR %s|%d", name, for_real);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
        return ret;
 }
 
@@ -1318,7 +1364,7 @@ int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
 /* KFLR */
 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
 {
-       char aaa[27];
+       char aaa[SIZ];
 
        if (!cret) return -1;
        if (floornum < 0) return -1;
@@ -1332,33 +1378,38 @@ int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
 {
        register int ret;
-       char *aaa;
+       char aaa[SIZ];
 
        if (!cret) return -2;
        if (!floorname) return -2;
        if (floornum < 0) return -2;
 
-       aaa = (char *)malloc(strlen(floorname) + 17);
-       if (!aaa) return -1;
-
        sprintf(aaa, "EFLR %d|%s", floornum, floorname);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
        return ret;
 }
 
 
-/* IDEN */
-int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid, int revision,
-               const char *software_name, const char *hostname, char *cret)
+/*
+ * IDEN 
+ *
+ * You only need to fill out hostname, the defaults will be used if any of the
+ * other fields are not set properly.
+ */
+int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
+               int revision, const char *software_name, const char *hostname,
+               char *cret)
 {
        register int ret;
        char *aaa;
 
-       if (developerid < 0) return -2;
-       if (clientid < 0) return -2;
-       if (revision < 0) return -2;
-       if (!software_name) return -2;
+       if (developerid < 0 || clientid < 0 || revision < 0 ||
+           !software_name) {
+               developerid = 8;
+               clientid = 0;
+               revision = REV_LEVEL - 600;
+               software_name = "Citadel/UX (libcitadel)";
+       }
        if (!hostname) return -2;
 
        aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
@@ -1612,14 +1663,14 @@ time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
 
 /* AGUP */
 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
-                                struct usersupp **uret, char *cret)
+                                struct ctdluser **uret, char *cret)
 {
        register int ret;
        char aaa[SIZ];
 
        if (!cret) return -2;
        if (!uret) return -2;
-       if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
+       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
        if (!*uret) return -1;
 
        sprintf(aaa, "AGUP %s", who);
@@ -1641,7 +1692,7 @@ int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
 
 
 /* ASUP */
-int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char *cret)
+int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
 {
        register int ret;
        char *aaa;
@@ -1664,39 +1715,54 @@ int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char
 
 /* GPEX */
 /* which is 0 = room, 1 = floor, 2 = site */
-int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which, char *cret)
+/* caller must free the struct ExpirePolicy */
+int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy **policy, char *cret)
 {
        static char *proto[] = {"room", "floor", "site"};
        char aaa[11];
+       register int ret;
 
        if (!cret) return -2;
+       if (!policy) return -2;
+       if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
+       if (!*policy) return -1;
        if (which < 0 || which > 2) return -2;
        
        sprintf(aaa, "GPEX %s", proto[which]);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               policy[0]->expire_mode = extract_int(cret, 0);
+               policy[0]->expire_value = extract_int(cret, 1);
+       }
+       return ret;
+
 }
 
 
 /* SPEX */
 /* which is 0 = room, 1 = floor, 2 = site */
 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
-int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which, int policy, int value,
-               char *cret)
+int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy *policy, char *cret)
 {
        char aaa[38];
+       char *whichvals[] = { "room", "floor", "site" };
 
        if (!cret) return -2;
        if (which < 0 || which > 2) return -2;
-       if (policy < 0 || policy > 3) return -2;
-       if (policy >= 2 && value < 1) return -2;
+       if (!policy) return -2;
+       if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
+       if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
 
-       sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
+       sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
+                       policy->expire_mode, policy->expire_value);
        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
 }
 
 
 /* CONF GET */
-int CtdlGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
+int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
 {
        size_t bytes;
 
@@ -1710,7 +1776,7 @@ int CtdlGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
 
 
 /* CONF SET */
-int CtdlSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
+int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
 {
        if (!cret) return -2;
        if (!listing) return -2;
@@ -1721,7 +1787,7 @@ int CtdlSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
 
 
 /* CONF GETSYS */
-int CtdlGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
                char **listing, char *cret)
 {
        char *aaa;
@@ -1741,7 +1807,7 @@ int CtdlGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
 
 
 /* CONF PUTSYS */
-int CtdlSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
               const char *listing, char *cret)
 {
        char *aaa;
@@ -1757,18 +1823,6 @@ int CtdlSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
                        NULL, NULL, cret);
 }
 
-/* MMOD */
-int CtdlIPCModerateMessage(CtdlIPC *ipc, long msgnum, int level, char *cret)
-{
-       char aaa[27];
-
-       if (!cret) return -2;
-       if (!msgnum) return -2;
-
-       sprintf(aaa, "MMOD %ld|%d", msgnum, level);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
 
 /* REQT */
 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
@@ -1917,6 +1971,19 @@ int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
 }
 
 
+/* FSCK */
+int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
+{
+       size_t size = 0;
+
+       if (!cret) return -2;
+       if (!mret) return -2;
+       if (*mret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
+}
+
+
 /*
  * Not implemented:
  * 
@@ -1941,6 +2008,7 @@ int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
 
 inline void CtdlIPC_lock(CtdlIPC *ipc)
 {
+       if (ipc->network_status_cb) ipc->network_status_cb(1);
 #ifdef THREADED_CLIENT
        pthread_mutex_lock(&(ipc->mutex));
 #endif
@@ -1952,6 +2020,7 @@ inline void CtdlIPC_unlock(CtdlIPC *ipc)
 #ifdef THREADED_CLIENT
        pthread_mutex_unlock(&(ipc->mutex));
 #endif
+       if (ipc->network_status_cb) ipc->network_status_cb(0);
 }
 
 
@@ -1960,15 +2029,13 @@ char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
 {
        size_t length = 0;
        size_t linelength;
-       char *ret;
+       char *ret = NULL;
        char aaa[SIZ];
 
        ret = dest;
        if (ret != NULL) {
                length = strlen(ret);
-       }
-       else {
-               ret = strdup("");
+       } else {
                length = 0;
        }
 
@@ -2016,13 +2083,13 @@ size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes,
        register size_t len = 0;
        char aaa[SIZ];
 
-       if (!buf) return -1;
-       if (!cret) return -1;
-       if (bytes < 1) return -1;
-       if (offset < 0) return -1;
+       if (!buf) return 0;
+       if (!cret) return 0;
+       if (bytes < 1) return 0;
+       if (offset < 0) return 0;
 
        CtdlIPC_lock(ipc);
-       sprintf(aaa, "READ %d|%d", offset, bytes);
+       sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
        CtdlIPC_putline(ipc, aaa);
        CtdlIPC_getline(ipc, aaa);
        if (aaa[0] != '6')
@@ -2036,7 +2103,7 @@ size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes,
                } else {
                        /* We have to read regardless */
                        serv_read(ipc, aaa, len);
-                       len = -1;
+                       len = 0;
                }
        }
        CtdlIPC_unlock(ipc);
@@ -2072,8 +2139,9 @@ int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
 
 
 /* READ */
-int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
-              void (*progress_gauge_callback)(long, long), char *cret)
+int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
+              void (*progress_gauge_callback)(unsigned long, unsigned long),
+              char *cret)
 {
        register size_t len;
 
@@ -2082,14 +2150,14 @@ int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
        if (*buf) return -1;
        if (!ipc->downloading) return -1;
 
-       len = 0;
+       len = resume;
        if (progress_gauge_callback)
                progress_gauge_callback(len, bytes);
        while (len < bytes) {
                register size_t block;
 
                block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
-               if (block == -1) {
+               if (block == 0) {
                        free(*buf);
                        return 0;
                }
@@ -2100,10 +2168,11 @@ int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
        return len;
 }
 
-
 /* READ - pipelined */
 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
-              void (*progress_gauge_callback)(long, long), char *cret)
+              size_t resume,
+              void (*progress_gauge_callback)(unsigned long, unsigned long),
+              char *cret)
 {
        register size_t len;
        register int calls;     /* How many calls in the pipeline */
@@ -2115,7 +2184,7 @@ int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
        if (*buf) return -1;
        if (!ipc->downloading) return -1;
 
-       *buf = (void *)realloc(*buf, bytes);
+       *buf = (void *)realloc(*buf, bytes - resume);
        if (!*buf) return -1;
 
        len = 0;
@@ -2124,12 +2193,12 @@ int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
                progress_gauge_callback(len, bytes);
 
        /* How many calls will be in the pipeline? */
-       calls = bytes / 4096;
-       if (bytes % 4096) calls++;
+       calls = (bytes - resume) / 4096;
+       if ((bytes - resume) % 4096) calls++;
 
        /* Send all requests at once */
        for (i = 0; i < calls; i++) {
-               sprintf(aaa, "READ %d|4096", i * 4096);
+               sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
                CtdlIPC_putline(ipc, aaa);
        }
 
@@ -2169,7 +2238,8 @@ int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
 
 /* WRIT */
 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
-               void (*progress_gauge_callback)(long, long), char *cret)
+               void (*progress_gauge_callback)(unsigned long, unsigned long),
+               char *cret)
 {
        register int ret = -1;
        register size_t offset = 0;
@@ -2200,7 +2270,7 @@ int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
                if (!to_write) {
                        if (feof(fd) || ferror(fd)) break;
                }
-               sprintf(aaa, "WRIT %d", to_write);
+               sprintf(aaa, "WRIT %d", (int)to_write);
                CtdlIPC_putline(ipc, aaa);
                CtdlIPC_getline(ipc, aaa);
                strcpy(cret, &aaa[4]);
@@ -2213,7 +2283,8 @@ int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
                        if (progress_gauge_callback)
                                progress_gauge_callback(offset, bytes);
                        /* Detect short reads and back up if needed */
-                       fseek(fd, offset, SEEK_SET);
+                       /* offset will never be negative anyway */
+                       fseek(fd, (signed)offset, SEEK_SET);
                } else {
                        break;
                }
@@ -2420,6 +2491,7 @@ static int connectsock(char *host, char *service, char *protocol, int defaultPor
        }
 
        if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+               close(s);
                return -1;
        }
 
@@ -2441,6 +2513,7 @@ static int uds_connectsock(int *isLocal, char *sockpath)
        }
 
        if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(s);
                return -1;
        }
 
@@ -2452,9 +2525,9 @@ static int uds_connectsock(int *isLocal, char *sockpath)
 /*
  * input binary data from socket
  */
-static void serv_read(CtdlIPC *ipc, char *buf, int bytes)
+static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
 {
-       int len, rlen;
+       unsigned int len, rlen;
 
 #if defined(HAVE_OPENSSL)
        if (ipc->ssl) {
@@ -2477,9 +2550,9 @@ static void serv_read(CtdlIPC *ipc, char *buf, int bytes)
 /*
  * send binary to server
  */
-static void serv_write(CtdlIPC *ipc, const char *buf, int nbytes)
+static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
 {
-       int bytes_written = 0;
+       unsigned int bytes_written = 0;
        int retval;
 
 #if defined(HAVE_OPENSSL)
@@ -2504,7 +2577,7 @@ static void serv_write(CtdlIPC *ipc, const char *buf, int nbytes)
 /*
  * input binary data from encrypted connection
  */
-static void serv_read_ssl(CtdlIPC* ipc, char *buf, int bytes)
+static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
 {
        int len, rlen;
        char junk[1];
@@ -2534,7 +2607,7 @@ static void serv_read_ssl(CtdlIPC* ipc, char *buf, int bytes)
                        }
                        error_printf("SSL_read in serv_read:\n");
                        ERR_print_errors_fp(stderr);
-                       connection_died();
+                       connection_died(ipc);
                        return;
                }
                len += rlen;
@@ -2545,9 +2618,9 @@ static void serv_read_ssl(CtdlIPC* ipc, char *buf, int bytes)
 /*
  * send binary to server encrypted
  */
-static void serv_write_ssl(CtdlIPC *ipc, const char *buf, int nbytes)
+static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
 {
-       int bytes_written = 0;
+       unsigned int bytes_written = 0;
        int retval;
        char junk[1];
 
@@ -2577,7 +2650,7 @@ static void serv_write_ssl(CtdlIPC *ipc, const char *buf, int nbytes)
                        }
                        error_printf("SSL_write in serv_write:\n");
                        ERR_print_errors_fp(stderr);
-                       connection_died();
+                       connection_died(ipc);
                        return;
                }
                bytes_written += retval;
@@ -2701,9 +2774,9 @@ void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
                while (buf[i] != '\n')
                        serv_read(ipc, &buf[i], 1);
 
-       /* Strip the trailing newline.
-        */
-       buf[i] = 0;
+       /* Strip the trailing newline (and carriage return, if present) */
+       if (buf[i] == 10) buf[i--] = 0;
+       if (buf[i] == 13) buf[i--] = 0;
 }
 
 
@@ -2746,10 +2819,18 @@ CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
        ipc->downloading = 0;
        ipc->uploading = 0;
        ipc->last_command_sent = 0L;
+       ipc->network_status_cb = NULL;
 
        strcpy(cithost, DEFAULT_HOST);  /* default host */
        strcpy(citport, DEFAULT_PORT);  /* default port */
 
+       /* Allow caller to supply our values (Windows) */
+       if (hostbuf && strlen(hostbuf) > 0)
+               strcpy(cithost, hostbuf);
+       if (portbuf && strlen(portbuf) > 0)
+               strcpy(citport, portbuf);
+
+       /* Read host/port from command line if present */
        for (a = 0; a < argc; ++a) {
                if (a == 0) {
                        /* do nothing */
@@ -2773,7 +2854,7 @@ CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
 
        /* If we're using a unix domain socket we can do a bunch of stuff */
        if (!strcmp(cithost, UDS)) {
-               snprintf(sockpath, sizeof sockpath, "citadel.socket");
+               snprintf(sockpath, sizeof sockpath, BBSDIR "/citadel.socket");
                ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
                if (ipc->sock == -1) {
                        ifree(ipc);