Back to index

citadel  8.12
citadel_ipc.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1987-2012 by the citadel.org team
00003  *
00004  *  This program is open source software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License version 3.
00006  *
00007  *  This program is distributed in the hope that it will be useful,
00008  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00009  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010  *  GNU General Public License for more details.
00011  */
00012 
00013 #include "sysdep.h"
00014 #if TIME_WITH_SYS_TIME
00015 # include <sys/time.h>
00016 # include <time.h>
00017 #else
00018 # if HAVE_SYS_TIME_H
00019 #  include <sys/time.h>
00020 # else
00021 #  include <time.h>
00022 # endif
00023 #endif
00024 #include <unistd.h>
00025 #include <stdio.h>
00026 #include <sys/types.h>
00027 #include <string.h>
00028 #ifdef HAVE_MALLOC_H
00029 #include <malloc.h>
00030 #endif
00031 #include <stdlib.h>
00032 #include <ctype.h>
00033 #include <sys/socket.h>
00034 #include <arpa/inet.h>
00035 #include <netinet/in.h>
00036 #include <netdb.h>
00037 #include <sys/un.h>
00038 #include <errno.h>
00039 #ifdef THREADED_CLIENT
00040 #include <pthread.h>
00041 #endif
00042 #include <libcitadel.h>
00043 #include "citadel.h"
00044 #include "citadel_ipc.h"
00045 #include "citadel_decls.h"
00046 #include "citadel_dirs.h"
00047 #ifdef THREADED_CLIENT
00048 pthread_mutex_t rwlock;
00049 #endif
00050 
00051 #ifdef HAVE_OPENSSL
00052 static SSL_CTX *ssl_ctx;
00053 char arg_encrypt;
00054 char rc_encrypt;
00055 #ifdef THREADED_CLIENT
00056 pthread_mutex_t **Critters;               /* Things that need locking */
00057 #endif /* THREADED_CLIENT */
00058 
00059 #endif /* HAVE_OPENSSL */
00060 
00061 #ifndef INADDR_NONE
00062 #define INADDR_NONE 0xffffffff
00063 #endif
00064 
00065 static void (*status_hook)(char *s) = NULL;
00066 
00067 void setCryptoStatusHook(void (*hook)(char *s)) {
00068        status_hook = hook;
00069 }
00070 
00071 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
00072        ipc->network_status_cb = hook;
00073 }
00074 
00075 
00076 char instant_msgs = 0;
00077 
00078 
00079 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
00080 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
00081 #ifdef HAVE_OPENSSL
00082 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
00083 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
00084 static void endtls(SSL *ssl);
00085 #ifdef THREADED_CLIENT
00086 static unsigned long id_callback(void);
00087 #endif /* THREADED_CLIENT */
00088 #endif /* HAVE_OPENSSL */
00089 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
00090 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
00091 
00092 
00093 
00094 const char *svn_revision(void);
00095 
00096 /*
00097  * Does nothing.  The server should always return 200.
00098  */
00099 int CtdlIPCNoop(CtdlIPC *ipc)
00100 {
00101        char aaa[128];
00102 
00103        return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
00104 }
00105 
00106 
00107 /*
00108  * Does nothing interesting.  The server should always return 200
00109  * along with your string.
00110  */
00111 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
00112 {
00113        register int ret;
00114        char *aaa;
00115        
00116        if (!arg) return -2;
00117        if (!cret) return -2;
00118 
00119        aaa = (char *)malloc((size_t)(strlen(arg) + 6));
00120        if (!aaa) return -1;
00121 
00122        sprintf(aaa, "ECHO %s", arg);
00123        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00124        free(aaa);
00125        return ret;
00126 }
00127 
00128 
00129 /*
00130  * Asks the server to close the connecction.
00131  * Should always return 200.
00132  */
00133 int CtdlIPCQuit(CtdlIPC *ipc)
00134 {
00135        register int ret = 221;            /* Default to successful quit */
00136        char aaa[SIZ]; 
00137 
00138        CtdlIPC_lock(ipc);
00139        if (ipc->sock > -1) {
00140               CtdlIPC_putline(ipc, "QUIT");
00141               CtdlIPC_getline(ipc, aaa);
00142               ret = atoi(aaa);
00143        }
00144 #ifdef HAVE_OPENSSL
00145        if (ipc->ssl)
00146               SSL_shutdown(ipc->ssl);
00147        ipc->ssl = NULL;
00148 #endif
00149        if (ipc->sock)
00150               shutdown(ipc->sock, 2);     /* Close connection; we're dead */
00151        ipc->sock = -1;
00152        CtdlIPC_unlock(ipc);
00153        return ret;
00154 }
00155 
00156 
00157 /*
00158  * Asks the server to log out.  Should always return 200, even if no user
00159  * was logged in.  The user will not be logged in after this!
00160  */
00161 int CtdlIPCLogout(CtdlIPC *ipc)
00162 {
00163        register int ret;
00164        char aaa[SIZ];
00165 
00166        CtdlIPC_lock(ipc);
00167        CtdlIPC_putline(ipc, "LOUT");
00168        CtdlIPC_getline(ipc, aaa);
00169        ret = atoi(aaa);
00170        CtdlIPC_unlock(ipc);
00171        return ret;
00172 }
00173 
00174 
00175 /*
00176  * First stage of authentication - pass the username.  Returns 300 if the
00177  * username is able to log in, with the username correctly spelled in cret.
00178  * Returns various 500 error codes if the user doesn't exist, etc.
00179  */
00180 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
00181 {
00182        register int ret;
00183        char *aaa;
00184 
00185        if (!username) return -2;
00186        if (!cret) return -2;
00187 
00188        aaa = (char *)malloc((size_t)(strlen(username) + 6));
00189        if (!aaa) return -1;
00190 
00191        sprintf(aaa, "USER %s", username);
00192        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00193        free(aaa);
00194        return ret;
00195 }
00196 
00197 
00198 /*
00199  * Second stage of authentication - provide password.  The server returns
00200  * 200 and several arguments in cret relating to the user's account.
00201  */
00202 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
00203 {
00204        register int ret;
00205        char *aaa;
00206 
00207        if (!passwd) return -2;
00208        if (!cret) return -2;
00209 
00210        aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
00211        if (!aaa) return -1;
00212 
00213        sprintf(aaa, "PASS %s", passwd);
00214        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00215        free(aaa);
00216        return ret;
00217 }
00218 
00219 
00220 /*
00221  * Second stage of authentication - provide password.  The server returns
00222  * 200 and several arguments in cret relating to the user's account.
00223  */
00224 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
00225 {
00226        register int ret;
00227        char *aaa;
00228 
00229        if (!response) return -2;
00230        if (!cret) return -2;
00231 
00232        aaa = (char *)malloc((size_t)(strlen(response) + 6));
00233        if (!aaa) return -1;
00234 
00235        sprintf(aaa, "PAS2 %s", response);
00236        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00237        free(aaa);
00238        return ret;
00239 }
00240 
00241 
00242 /*
00243  * Create a new user.  This returns 200 plus the same arguments as TryPassword
00244  * if selfservice is nonzero, unless there was a problem creating the account.
00245  * If selfservice is zero, creates a new user but does not log out the existing
00246  * user - intended for use by system administrators to create accounts on
00247  * behalf of other users.
00248  */
00249 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
00250 {
00251        register int ret;
00252        char *aaa;
00253 
00254        if (!username) return -2;
00255        if (!cret) return -2;
00256 
00257        aaa = (char *)malloc((size_t)(strlen(username) + 6));
00258        if (!aaa) return -1;
00259 
00260        sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
00261        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00262        free(aaa);
00263        return ret;
00264 }
00265 
00266 
00267 /*
00268  * Changes the user's password.  Returns 200 if changed, errors otherwise.
00269  */
00270 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
00271 {
00272        register int ret;
00273        char *aaa;
00274 
00275        if (!passwd) return -2;
00276        if (!cret) return -2;
00277 
00278        aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
00279        if (!aaa) return -1;
00280 
00281        sprintf(aaa, "SETP %s", passwd);
00282        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00283        free(aaa);
00284        return ret;
00285 }
00286 
00287 
00288 /* LKRN */
00289 /* Caller must free the march list */
00290 /* Room types are defined in enum RoomList; keep these in sync! */
00291 /* floor is -1 for all, or floornum */
00292 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
00293 {
00294        register int ret;
00295        struct march *march = NULL;
00296        static char *proto[] =
00297               {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
00298        char aaa[SIZ];
00299        char *bbb = NULL;
00300        size_t bbb_len;
00301 
00302        if (!listing) return -2;
00303        if (*listing) return -2;    /* Free the listing first */
00304        if (!cret) return -2;
00305        /* if (which < 0 || which > 4) return -2; */
00306        if (floor < -1) return -2;  /* Can't validate upper bound, sorry */
00307 
00308        sprintf(aaa, "%s %d", proto[which], floor);
00309        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
00310        if (ret / 100 == 1) {
00311               struct march *mptr;
00312 
00313               while (bbb && strlen(bbb)) {
00314                      int a;
00315 
00316                      extract_token(aaa, bbb, 0, '\n', sizeof aaa);
00317                      a = strlen(aaa);
00318                      memmove(bbb, bbb + a + 1, strlen(bbb) - a);
00319                      mptr = (struct march *) malloc(sizeof (struct march));
00320                      if (mptr) {
00321                             mptr->next = NULL;
00322                             extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
00323                             mptr->march_flags = (unsigned int) extract_int(aaa, 1);
00324                             mptr->march_floor = (char) extract_int(aaa, 2);
00325                             mptr->march_order = (char) extract_int(aaa, 3);
00326                             mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
00327                             mptr->march_access = (char) extract_int(aaa, 5);
00328                             if (march == NULL)
00329                                    march = mptr;
00330                             else {
00331                                    struct march *mptr2;
00332 
00333                                    mptr2 = march;
00334                                    while (mptr2->next != NULL)
00335                                           mptr2 = mptr2->next;
00336                                    mptr2->next = mptr;
00337                             }
00338                      }
00339               }
00340        }
00341        *listing = march;
00342        if (bbb) free(bbb);
00343        return ret;
00344 }
00345 
00346 
00347 /* GETU */
00348 /* Caller must free the struct ctdluser; caller may pass an existing one */
00349 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
00350 {
00351        register int ret;
00352 
00353        if (!cret) return -2;
00354        if (!uret) return -2;
00355        if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
00356        if (!*uret) return -1;
00357 
00358        ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
00359        if (ret / 100 == 2) {
00360               uret[0]->flags = extract_int(cret, 2);
00361        }
00362        return ret;
00363 }
00364 
00365 
00366 /* SETU */
00367 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
00368 {
00369        char aaa[48];
00370 
00371        if (!uret) return -2;
00372        if (!cret) return -2;
00373 
00374        sprintf(aaa,
00375               "SETU 80|24|%d",
00376               uret->flags
00377        );
00378        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00379 }
00380 
00381 
00382 /* RENU */
00383 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
00384 {
00385        register int ret;
00386        char cmd[256];
00387 
00388        if (!oldname) return -2;
00389        if (!newname) return -2;
00390        if (!cret) return -2;
00391 
00392        snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
00393        ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
00394        return ret;
00395 }
00396 
00397 
00398 /* GOTO */
00399 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
00400               struct ctdlipcroom **rret, char *cret)
00401 {
00402        register int ret;
00403        char *aaa;
00404 
00405        if (!cret) return -2;
00406        if (!rret) return -2;
00407        if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
00408        if (!*rret) return -1;
00409 
00410        if (passwd) {
00411               aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
00412               if (!aaa) {
00413                      free(*rret);
00414                      return -1;
00415               }
00416               sprintf(aaa, "GOTO %s|%s", room, passwd);
00417        } else {
00418               aaa = (char *)malloc(strlen(room) + 6);
00419               if (!aaa) {
00420                      free(*rret);
00421                      return -1;
00422               }
00423               sprintf(aaa, "GOTO %s", room);
00424        }
00425        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00426        if (ret / 100 == 2) {
00427               extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
00428               rret[0]->RRunread = extract_long(cret, 1);
00429               rret[0]->RRtotal = extract_long(cret, 2);
00430               rret[0]->RRinfoupdated = extract_int(cret, 3);
00431               rret[0]->RRflags = extract_int(cret, 4);
00432               rret[0]->RRhighest = extract_long(cret, 5);
00433               rret[0]->RRlastread = extract_long(cret, 6);
00434               rret[0]->RRismailbox = extract_int(cret, 7);
00435               rret[0]->RRaide = extract_int(cret, 8);
00436               rret[0]->RRnewmail = extract_long(cret, 9);
00437               rret[0]->RRfloor = extract_int(cret, 10);
00438               rret[0]->RRcurrentview = extract_int(cret, 11);
00439               rret[0]->RRdefaultview = extract_int(cret, 12);
00440               /* position 13 is a trash folder flag ... irrelevant in this client */
00441               rret[0]->RRflags2 = extract_int(cret, 14);
00442        } else {
00443               free(*rret);
00444               *rret = NULL;
00445        }
00446        free(aaa);
00447        return ret;
00448 }
00449 
00450 
00451 /* MSGS */
00452 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
00453 /* whicharg is number of messages, applies to last, first, gt, lt */
00454 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
00455               const char *mtemplate, unsigned long **mret, char *cret)
00456 {
00457        register int ret;
00458        register unsigned long count = 0;
00459        static char *proto[] =
00460               { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
00461        char aaa[33];
00462        char *bbb = NULL;
00463        size_t bbb_len;
00464 
00465        if (!cret) return -2;
00466        if (!mret) return -2;
00467        if (*mret) return -2;
00468        if (which < 0 || which > 6) return -2;
00469 
00470        if (which <= 2)
00471               sprintf(aaa, "MSGS %s||%d", proto[which],
00472                             (mtemplate) ? 1 : 0);
00473        else
00474               sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
00475                             (mtemplate) ? 1 : 0);
00476        if (mtemplate) count = strlen(mtemplate);
00477        ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
00478        if (ret / 100 != 1)
00479               return ret;
00480        count = 0;
00481        *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
00482        if (!*mret)
00483               return -1;
00484        while (bbb && strlen(bbb)) {
00485               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
00486               remove_token(bbb, 0, '\n');
00487               *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
00488                                    sizeof (unsigned long)));
00489               if (*mret) {
00490                      (*mret)[count++] = atol(aaa);
00491                      (*mret)[count] = 0L;
00492               } else {
00493                      break;
00494               }
00495        }
00496        if (bbb) free(bbb);
00497        return ret;
00498 }
00499 
00500 
00501 /* MSG0, MSG2 */
00502 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
00503               struct ctdlipcmessage **mret, char *cret)
00504 {
00505        register int ret;
00506        char aaa[SIZ];
00507        char *bbb = NULL;
00508        size_t bbb_len;
00509        int multipart_hunting = 0;
00510        char multipart_prefix[128];
00511        char encoding[256];
00512 
00513        if (!cret) return -1;
00514        if (!mret) return -1;
00515        if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
00516        if (!*mret) return -1;
00517        if (!msgnum) return -1;
00518 
00519        strcpy(encoding, "");
00520        strcpy(mret[0]->content_type, "");
00521        sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
00522        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
00523        if (ret / 100 == 1) {
00524               if (as_mime != 2) {
00525                      strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
00526                      while (strlen(bbb) > 4 && bbb[4] == '=') {
00527                             extract_token(aaa, bbb, 0, '\n', sizeof aaa);
00528                             remove_token(bbb, 0, '\n');
00529 
00530                             if (!strncasecmp(aaa, "nhdr=yes", 8))
00531                                    mret[0]->nhdr = 1;
00532                             else if (!strncasecmp(aaa, "from=", 5))
00533                                    safestrncpy(mret[0]->author, &aaa[5], SIZ);
00534                             else if (!strncasecmp(aaa, "type=", 5))
00535                                    mret[0]->type = atoi(&aaa[5]);
00536                             else if (!strncasecmp(aaa, "msgn=", 5))
00537                                    safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
00538                             else if (!strncasecmp(aaa, "subj=", 5))
00539                                    safestrncpy(mret[0]->subject, &aaa[5], SIZ);
00540                             else if (!strncasecmp(aaa, "rfca=", 5))
00541                                    safestrncpy(mret[0]->email, &aaa[5], SIZ);
00542                             else if (!strncasecmp(aaa, "hnod=", 5))
00543                                    safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
00544                             else if (!strncasecmp(aaa, "room=", 5))
00545                                    safestrncpy(mret[0]->room, &aaa[5], SIZ);
00546                             else if (!strncasecmp(aaa, "node=", 5))
00547                                    safestrncpy(mret[0]->node, &aaa[5], SIZ);
00548                             else if (!strncasecmp(aaa, "rcpt=", 5))
00549                                    safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
00550                             else if (!strncasecmp(aaa, "wefw=", 5))
00551                                    safestrncpy(mret[0]->references, &aaa[5], SIZ);
00552                             else if (!strncasecmp(aaa, "time=", 5))
00553                                    mret[0]->time = atol(&aaa[5]);
00554 
00555                             /* Multipart/alternative prefix & suffix strings help
00556                              * us to determine which part we want to download.
00557                              */
00558                             else if (!strncasecmp(aaa, "pref=", 5)) {
00559                                    extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
00560                                    if (!strcasecmp(multipart_prefix,
00561                                       "multipart/alternative")) {
00562                                           ++multipart_hunting;
00563                                    }
00564                             }
00565                             else if (!strncasecmp(aaa, "suff=", 5)) {
00566                                    extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
00567                                    if (!strcasecmp(multipart_prefix,
00568                                       "multipart/alternative")) {
00569                                           ++multipart_hunting;
00570                                    }
00571                             }
00572 
00573                             else if (!strncasecmp(aaa, "part=", 5)) {
00574                                    struct parts *ptr, *chain;
00575        
00576                                    ptr = (struct parts *)calloc(1, sizeof (struct parts));
00577                                    if (ptr) {
00578 
00579                                           /* Fill the buffers for the caller */
00580                                           extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
00581                                           extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
00582                                           extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
00583                                           extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
00584                                           extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
00585                                           ptr->length = extract_long(&aaa[5], 5);
00586                                           if (!mret[0]->attachments)
00587                                                  mret[0]->attachments = ptr;
00588                                           else {
00589                                                  chain = mret[0]->attachments;
00590                                                  while (chain->next)
00591                                                         chain = chain->next;
00592                                                  chain->next = ptr;
00593                                           }
00594 
00595                                           /* Now handle multipart/alternative */
00596                                           if (multipart_hunting > 0) {
00597                                                  if ( (!strcasecmp(ptr->mimetype,
00598                                                       "text/plain"))
00599                                                     || (!strcasecmp(ptr->mimetype,
00600                                                        "text/html")) ) {
00601                                                         strcpy(mret[0]->mime_chosen,
00602                                                                ptr->number);
00603                                                  }
00604                                           }
00605 
00606                                    }
00607                             }
00608                      }
00609                      /* Eliminate "text\n" */
00610                      remove_token(bbb, 0, '\n');
00611 
00612                      /* If doing a MIME thing, pull out the extra headers */
00613                      if (as_mime == 4) {
00614                             do {
00615                                    if (!strncasecmp(bbb, "Content-type:", 13)) {
00616                                           extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
00617                                           strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
00618                                           striplt(mret[0]->content_type);
00619 
00620                                           /* strip out ";charset=" portion.  FIXME do something with
00621                                            * the charset (like... convert it) instead of just throwing
00622                                            * it away
00623                                            */
00624                                           if (strstr(mret[0]->content_type, ";") != NULL) {
00625                                                  strcpy(strstr(mret[0]->content_type, ";"), "");
00626                                           }
00627 
00628                                    }
00629                                    if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
00630                                           extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
00631                                           strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
00632                                           striplt(mret[0]->mime_chosen);
00633                                    }
00634                                    if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
00635                                           extract_token(encoding, bbb, 0, '\n', sizeof encoding);
00636                                           strcpy(encoding, &encoding[26]);
00637                                           striplt(encoding);
00638                                    }
00639                                    remove_token(bbb, 0, '\n');
00640                             } while ((bbb[0] != 0) && (bbb[0] != '\n'));
00641                             remove_token(bbb, 0, '\n');
00642                      }
00643 
00644 
00645               }
00646               if (strlen(bbb)) {
00647 
00648                      if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
00649                             char *ccc = NULL;
00650                             int bytes_decoded = 0;
00651                             ccc = malloc(strlen(bbb) + 32768);
00652                             if (!strcasecmp(encoding, "base64")) {
00653                                    bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
00654                             }
00655                             else if (!strcasecmp(encoding, "quoted-printable")) {
00656                                    bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
00657                             }
00658                             ccc[bytes_decoded] = 0;
00659                             free(bbb);
00660                             bbb = ccc;
00661                      }
00662 
00663                      /* FIXME: Strip trailing whitespace */
00664                      bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
00665 
00666               } else {
00667                      bbb = (char *)realloc(bbb, 1);
00668                      *bbb = '\0';
00669               }
00670               mret[0]->text = bbb;
00671        }
00672        return ret;
00673 }
00674 
00675 
00676 /* WHOK */
00677 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
00678 {
00679        register int ret;
00680        size_t bytes;
00681 
00682        if (!cret) return -2;
00683        if (!listing) return -2;
00684        if (*listing) return -2;
00685 
00686        ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
00687        return ret;
00688 }
00689 
00690 
00691 /* INFO */
00692 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
00693 {
00694        register int ret;
00695        size_t bytes;
00696        char *listing = NULL;
00697        char buf[SIZ];
00698 
00699        if (!cret) return -2;
00700 
00701        ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
00702        if (ret / 100 == 1) {
00703               int line = 0;
00704 
00705               while (*listing && strlen(listing)) {
00706                      extract_token(buf, listing, 0, '\n', sizeof buf);
00707                      remove_token(listing, 0, '\n');
00708                      switch (line++) {
00709                      case 0:              ipc->ServInfo.pid = atoi(buf);
00710                                    break;
00711                      case 1:              strcpy(ipc->ServInfo.nodename,buf);
00712                                    break;
00713                      case 2:              strcpy(ipc->ServInfo.humannode,buf);
00714                                    break;
00715                      case 3:              strcpy(ipc->ServInfo.fqdn,buf);
00716                                    break;
00717                      case 4:              strcpy(ipc->ServInfo.software,buf);
00718                                    break;
00719                      case 5:              ipc->ServInfo.rev_level = atoi(buf);
00720                                    break;
00721                      case 6:              strcpy(ipc->ServInfo.site_location,buf);
00722                                    break;
00723                      case 7:              strcpy(ipc->ServInfo.sysadm,buf);
00724                                    break;
00725                      case 9:              strcpy(ipc->ServInfo.moreprompt,buf);
00726                                    break;
00727                      case 10:      ipc->ServInfo.ok_floors = atoi(buf);
00728                                    break;
00729                      case 11:      ipc->ServInfo.paging_level = atoi(buf);
00730                                    break;
00731                      case 13:      ipc->ServInfo.supports_qnop = atoi(buf);
00732                                    break;
00733                      case 14:      ipc->ServInfo.supports_ldap = atoi(buf);
00734                                    break;
00735                      case 15:      ipc->ServInfo.newuser_disabled = atoi(buf);
00736                                    break;
00737                      case 16:      strcpy(ipc->ServInfo.default_cal_zone, buf);
00738                                    break;
00739                      case 17:      ipc->ServInfo.load_avg = atof(buf);
00740                                    break;
00741                      case 18:      ipc->ServInfo.worker_avg = atof(buf);
00742                                    break;
00743                      case 19:      ipc->ServInfo.thread_count = atoi(buf);
00744                                    break;
00745                      case 20:      ipc->ServInfo.has_sieve = atoi(buf);
00746                                    break;
00747                      case 21:      ipc->ServInfo.fulltext_enabled = atoi(buf);
00748                                    break;
00749                      case 22:      strcpy(ipc->ServInfo.svn_revision, buf);
00750                                    break;
00751                      case 24:      ipc->ServInfo.guest_logins = atoi(buf);
00752                                    break;
00753                      }
00754               }
00755 
00756        }
00757        if (listing) free(listing);
00758        return ret;
00759 }
00760 
00761 
00762 /* RDIR */
00763 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
00764 {
00765        register int ret;
00766        size_t bytes;
00767 
00768        if (!cret) return -2;
00769        if (!listing) return -2;
00770        if (*listing) return -2;
00771 
00772        ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
00773        return ret;
00774 }
00775 
00776 
00777 /*
00778  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
00779  */
00780 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
00781 {
00782        register int ret;
00783        char aaa[64];
00784 
00785        if (!cret) return -2;
00786 
00787        if (msgnum) {
00788               sprintf(aaa, "SLRP %ld", msgnum);
00789        }
00790        else {
00791               sprintf(aaa, "SLRP HIGHEST");
00792        }
00793        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00794        return ret;
00795 }
00796 
00797 
00798 /* INVT */
00799 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
00800 {
00801        register int ret;
00802        char *aaa;
00803 
00804        if (!cret) return -2;
00805        if (!username) return -2;
00806 
00807        aaa = (char *)malloc(strlen(username) + 6);
00808        if (!aaa) return -1;
00809 
00810        sprintf(aaa, "INVT %s", username);
00811        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00812        free(aaa);
00813        return ret;
00814 }
00815 
00816 
00817 /* KICK */
00818 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
00819 {
00820        register int ret;
00821        char *aaa;
00822 
00823        if (!cret) return -1;
00824        if (!username) return -1;
00825 
00826        aaa = (char *)malloc(strlen(username) + 6);
00827 
00828        sprintf(aaa, "KICK %s", username);
00829        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00830        free(aaa);
00831        return ret;
00832 }
00833 
00834 
00835 /* GETR */
00836 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
00837 {
00838        register int ret;
00839 
00840        if (!cret) return -2;
00841        if (!qret) return -2;
00842        if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
00843        if (!*qret) return -1;
00844 
00845        ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
00846        if (ret / 100 == 2) {
00847               extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
00848               extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
00849               extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
00850               qret[0]->QRflags = extract_int(cret, 3);
00851               qret[0]->QRfloor = extract_int(cret, 4);
00852               qret[0]->QRorder = extract_int(cret, 5);
00853               qret[0]->QRdefaultview = extract_int(cret, 6);
00854               qret[0]->QRflags2 = extract_int(cret, 7);
00855        }
00856        return ret;
00857 }
00858 
00859 
00860 /* SETR */
00861 /* set forget to kick all users out of room */
00862 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
00863 {
00864        register int ret;
00865        char *aaa;
00866 
00867        if (!cret) return -2;
00868        if (!qret) return -2;
00869 
00870        aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
00871                      strlen(qret->QRdirname) + 64);
00872        if (!aaa) return -1;
00873 
00874        sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
00875                      qret->QRname, qret->QRpasswd, qret->QRdirname,
00876                      qret->QRflags, forget, qret->QRfloor, qret->QRorder,
00877                      qret->QRdefaultview, qret->QRflags2);
00878        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00879        free(aaa);
00880        return ret;
00881 }
00882 
00883 
00884 /* GETA */
00885 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
00886 {
00887        if (!cret) return -1;
00888 
00889        return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
00890 }
00891 
00892 
00893 /* SETA */
00894 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
00895 {
00896        register int ret;
00897        char *aaa;
00898 
00899        if (!cret) return -2;
00900        if (!username) return -2;
00901 
00902        aaa = (char *)malloc(strlen(username) + 6);
00903        if (!aaa) return -1;
00904 
00905        sprintf(aaa, "SETA %s", username);
00906        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00907        free(aaa);
00908        return ret;
00909 }
00910 
00911 
00912 /* ENT0 */
00913 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required,  struct ctdlipcmessage *mr, char *cret)
00914 {
00915        register int ret;
00916        char cmd[SIZ];
00917        char *ptr;
00918 
00919        if (!cret) return -2;
00920        if (!mr) return -2;
00921 
00922        if (mr->references) {
00923               for (ptr=mr->references; *ptr != 0; ++ptr) {
00924                      if (*ptr == '|') *ptr = '!';
00925               }
00926        }
00927 
00928        snprintf(cmd, sizeof cmd,
00929                      "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
00930                      mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
00931        ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
00932                      NULL, cret);
00933        if ((flag == 0) && (subject_required != NULL)) {
00934               /* Is the server strongly recommending that the user enter a message subject? */
00935               if ((cret[3] != '\0') && (cret[4] != '\0')) {
00936                      *subject_required = extract_int(&cret[4], 1);
00937               }
00938 
00939               
00940        }
00941        return ret;
00942 }
00943 
00944 
00945 /* RINF */
00946 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
00947 {
00948        size_t bytes;
00949 
00950        if (!cret) return -2;
00951        if (!iret) return -2;
00952        if (*iret) return -2;
00953 
00954        return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
00955 }
00956 
00957 
00958 /* DELE */
00959 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
00960 {
00961        char aaa[64];
00962 
00963        if (!cret) return -2;
00964        if (!msgnum) return -2;
00965 
00966        sprintf(aaa, "DELE %ld", msgnum);
00967        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00968 }
00969 
00970 
00971 /* MOVE */
00972 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
00973 {
00974        register int ret;
00975        char *aaa;
00976 
00977        if (!cret) return -2;
00978        if (!destroom) return -2;
00979        if (!msgnum) return -2;
00980 
00981        aaa = (char *)malloc(strlen(destroom) + 28);
00982        if (!aaa) return -1;
00983 
00984        sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
00985        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
00986        free(aaa);
00987        return ret;
00988 }
00989 
00990 
00991 /* KILL */
00992 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
00993 {
00994        char aaa[64];
00995 
00996        if (!cret) return -2;
00997 
00998        sprintf(aaa, "KILL %d", for_real);
00999        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01000 }
01001 
01002 
01003 /* CRE8 */
01004 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
01005               const char *password, int floor, char *cret)
01006 {
01007        register int ret;
01008        char *aaa;
01009 
01010        if (!cret) return -2;
01011        if (!roomname) return -2;
01012 
01013        if (password) {
01014               aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
01015               if (!aaa) return -1;
01016               sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
01017                             password, floor);
01018        } else {
01019               aaa = (char *)malloc(strlen(roomname) + 40);
01020               if (!aaa) return -1;
01021               sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
01022                             floor);
01023        }
01024        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01025        free(aaa);
01026        return ret;
01027 }
01028 
01029 
01030 /* FORG */
01031 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
01032 {
01033        if (!cret) return -2;
01034 
01035        return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
01036 }
01037 
01038 
01039 /* MESG */
01040 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
01041 {
01042        register int ret;
01043        char *aaa;
01044        size_t bytes;
01045 
01046        if (!cret) return -2;
01047        if (!mret) return -2;
01048        if (*mret) return -2;
01049        if (!message) return -2;
01050 
01051        aaa = (char *)malloc(strlen(message) + 6);
01052        if (!aaa) return -1;
01053 
01054        sprintf(aaa, "MESG %s", message);
01055        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
01056        free(aaa);
01057        return ret;
01058 }
01059 
01060 
01061 /* GNUR */
01062 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
01063 {
01064        if (!cret) return -2;
01065 
01066        return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
01067 }
01068 
01069 
01070 /* GREG */
01071 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
01072 {
01073        register int ret;
01074        char *aaa;
01075        size_t bytes;
01076 
01077        if (!cret) return -2;
01078        if (!rret) return -2;
01079        if (*rret) return -2;
01080 
01081        if (username)
01082               aaa = (char *)malloc(strlen(username) + 6);
01083        else
01084               aaa = (char *)malloc(12);
01085        if (!aaa) return -1;
01086 
01087        if (username)
01088               sprintf(aaa, "GREG %s", username);
01089        else
01090               sprintf(aaa, "GREG _SELF_");
01091        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
01092        free(aaa);
01093        return ret;
01094 }
01095 
01096 
01097 /* VALI */
01098 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
01099 {
01100        register int ret;
01101        char *aaa;
01102 
01103        if (!cret) return -2;
01104        if (!username) return -2;
01105        if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
01106 
01107        aaa = (char *)malloc(strlen(username) + 17);
01108        if (!aaa) return -1;
01109 
01110        sprintf(aaa, "VALI %s|%d", username, axlevel);
01111        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01112        free(aaa);
01113        return ret;
01114 }
01115 
01116 
01117 /* EINF */
01118 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
01119 {
01120        char aaa[64];
01121 
01122        if (!cret) return -1;
01123        if (!info) return -1;
01124 
01125        sprintf(aaa, "EINF %d", for_real);
01126        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01127 }
01128 
01129 
01130 /* LIST */
01131 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
01132 {
01133        size_t bytes;
01134        char *cmd;
01135        int ret;
01136 
01137        if (!cret) return -1;
01138        if (!listing) return -1;
01139        if (*listing) return -1;
01140        if (!searchstring) return -1;
01141 
01142        cmd = malloc(strlen(searchstring) + 10);
01143        sprintf(cmd, "LIST %s", searchstring);
01144 
01145        ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
01146        free(cmd);
01147        return(ret);
01148 }
01149 
01150 
01151 /* REGI */
01152 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
01153 {
01154        if (!cret) return -1;
01155        if (!info) return -1;
01156 
01157        return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
01158                      NULL, NULL, cret);
01159 }
01160 
01161 
01162 /* CHEK */
01163 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
01164 {
01165        register int ret;
01166 
01167        if (!cret) return -1;
01168        if (!chek) return -1;
01169 
01170        ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
01171        if (ret / 100 == 2) {
01172               chek->newmail = extract_long(cret, 0);
01173               chek->needregis = extract_int(cret, 1);
01174               chek->needvalid = extract_int(cret, 2);
01175        }
01176        return ret;
01177 }
01178 
01179 
01180 /* DELF */
01181 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
01182 {
01183        register int ret;
01184        char *aaa;
01185 
01186        if (!cret) return -2;
01187        if (!filename) return -2;
01188        
01189        aaa = (char *)malloc(strlen(filename) + 6);
01190        if (!aaa) return -1;
01191 
01192        sprintf(aaa, "DELF %s", filename);
01193        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01194        free(aaa);
01195        return ret;
01196 }
01197 
01198 
01199 /* MOVF */
01200 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
01201 {
01202        register int ret;
01203        char *aaa;
01204 
01205        if (!cret) return -2;
01206        if (!filename) return -2;
01207        if (!destroom) return -2;
01208 
01209        aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
01210        if (!aaa) return -1;
01211 
01212        sprintf(aaa, "MOVF %s|%s", filename, destroom);
01213        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01214        free(aaa);
01215        return ret;
01216 }
01217 
01218 
01219 /* RWHO */
01220 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
01221 {
01222        register int ret;
01223        size_t bytes;
01224 
01225        if (!cret) return -1;
01226        if (!listing) return -1;
01227        if (*listing) return -1;
01228 
01229        *stamp = CtdlIPCServerTime(ipc, cret);
01230        if (!*stamp)
01231               *stamp = time(NULL);
01232        ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
01233        return ret;
01234 }
01235 
01236 
01237 /* OPEN */
01238 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
01239               size_t resume,
01240               void (*progress_gauge_callback)
01241                      (CtdlIPC*, unsigned long, unsigned long),
01242               char *cret)
01243 {
01244        register int ret;
01245        size_t bytes;
01246        time_t last_mod;
01247        char mimetype[SIZ];
01248        char *aaa;
01249 
01250        if (!cret) return -2;
01251        if (!filename) return -2;
01252        if (!buf) return -2;
01253        if (*buf) return -2;
01254        if (ipc->downloading) return -2;
01255 
01256        aaa = (char *)malloc(strlen(filename) + 6);
01257        if (!aaa) return -1;
01258 
01259        sprintf(aaa, "OPEN %s", filename);
01260        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01261        free(aaa);
01262        if (ret / 100 == 2) {
01263               ipc->downloading = 1;
01264               bytes = extract_long(cret, 0);
01265               last_mod = extract_int(cret, 1);
01266               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
01267 
01268               ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
01269                                    progress_gauge_callback, cret);
01270               /*
01271               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
01272                                    progress_gauge_callback, cret);
01273               */
01274 
01275               ret = CtdlIPCEndDownload(ipc, cret);
01276               if (ret / 100 == 2)
01277                      sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
01278                                    filename, mimetype);
01279        }
01280        return ret;
01281 }
01282 
01283 
01284 /* OPNA */
01285 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
01286               void **buf,
01287               void (*progress_gauge_callback)
01288                      (CtdlIPC*, unsigned long, unsigned long),
01289               char *cret)
01290 {
01291        register int ret;
01292        size_t bytes;
01293        time_t last_mod;
01294        char filename[SIZ];
01295        char mimetype[SIZ];
01296        char aaa[SIZ];
01297 
01298        if (!cret) return -2;
01299        if (!buf) return -2;
01300        if (*buf) return -2;
01301        if (!part) return -2;
01302        if (!msgnum) return -2;
01303        if (ipc->downloading) return -2;
01304 
01305        sprintf(aaa, "OPNA %ld|%s", msgnum, part);
01306        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01307        if (ret / 100 == 2) {
01308               ipc->downloading = 1;
01309               bytes = extract_long(cret, 0);
01310               last_mod = extract_int(cret, 1);
01311               extract_token(filename, cret, 2, '|', sizeof filename);
01312               extract_token(mimetype, cret, 3, '|', sizeof mimetype);
01313               /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
01314               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
01315               ret = CtdlIPCEndDownload(ipc, cret);
01316               if (ret / 100 == 2)
01317                      sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
01318                                    filename, mimetype);
01319        }
01320        return ret;
01321 }
01322 
01323 
01324 /* OIMG */
01325 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
01326               void (*progress_gauge_callback)
01327                      (CtdlIPC*, unsigned long, unsigned long),
01328               char *cret)
01329 {
01330        register int ret;
01331        size_t bytes;
01332        time_t last_mod;
01333        char mimetype[SIZ];
01334        char *aaa;
01335 
01336        if (!cret) return -1;
01337        if (!buf) return -1;
01338        if (*buf) return -1;
01339        if (!filename) return -1;
01340        if (ipc->downloading) return -1;
01341 
01342        aaa = (char *)malloc(strlen(filename) + 6);
01343        if (!aaa) return -1;
01344 
01345        sprintf(aaa, "OIMG %s", filename);
01346        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01347        free(aaa);
01348        if (ret / 100 == 2) {
01349               ipc->downloading = 1;
01350               bytes = extract_long(cret, 0);
01351               last_mod = extract_int(cret, 1);
01352               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
01353 /*            ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
01354               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
01355               ret = CtdlIPCEndDownload(ipc, cret);
01356               if (ret / 100 == 2)
01357                      sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
01358                                    filename, mimetype);
01359        }
01360        return ret;
01361 }
01362 
01363 
01364 /* UOPN */
01365 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment, 
01366               const char *path, 
01367               void (*progress_gauge_callback)
01368                      (CtdlIPC*, unsigned long, unsigned long),
01369               char *cret)
01370 {
01371        register int ret;
01372        char *aaa;
01373        FILE *uploadFP;
01374        char MimeTestBuf[64];
01375        const char *MimeType;
01376        long len;
01377 
01378        if (!cret) return -1;
01379        if (!save_as) return -1;
01380        if (!comment) return -1;
01381        if (!path) return -1;
01382        if (!*path) return -1;
01383        if (ipc->uploading) return -1;
01384 
01385        uploadFP = fopen(path, "r");
01386        if (!uploadFP) return -2;
01387 
01388        len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
01389        rewind (uploadFP);
01390        if (len < 0) 
01391               return -3;
01392 
01393        MimeType = GuessMimeType(&MimeTestBuf[0], len);
01394        aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
01395        if (!aaa) return -1;
01396 
01397        sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType,  comment);
01398        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01399        free(aaa);
01400        if (ret / 100 == 2) {
01401               ipc->uploading = 1;
01402               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
01403               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
01404               ipc->uploading = 0;
01405        }
01406        return ret;
01407 }
01408 
01409 
01410 /* UIMG */
01411 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
01412               const char *save_as,
01413               void (*progress_gauge_callback)
01414                      (CtdlIPC*, unsigned long, unsigned long),
01415               char *cret)
01416 {
01417        register int ret;
01418        FILE *uploadFP;
01419        char *aaa;
01420        char MimeTestBuf[64];
01421        const char *MimeType;
01422        long len;
01423 
01424        if (!cret) return -1;
01425        if (!save_as) return -1;
01426        if (!path && for_real) return -1;
01427        if (!*path && for_real) return -1;
01428        if (ipc->uploading) return -1;
01429 
01430        aaa = (char *)malloc(strlen(save_as) + 17);
01431        if (!aaa) return -1;
01432 
01433        uploadFP = fopen(path, "r");
01434        if (!uploadFP) return -2;
01435 
01436        len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
01437        rewind (uploadFP);
01438        if (len < 0) 
01439               return -3;
01440        MimeType = GuessMimeType(&MimeTestBuf[0], 64);
01441 
01442        sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
01443        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01444        free(aaa);
01445        if (ret / 100 == 2 && for_real) {
01446               ipc->uploading = 1;
01447               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
01448               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
01449               ipc->uploading = 0;
01450        }
01451        return ret;
01452 }
01453 
01454 
01455 /* QUSR */
01456 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
01457 {
01458        register int ret;
01459        char *aaa;
01460 
01461        if (!cret) return -2;
01462        if (!username) return -2;
01463 
01464        aaa = (char *)malloc(strlen(username) + 6);
01465        if (!aaa) return -1;
01466 
01467        sprintf(aaa, "QUSR %s", username);
01468        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01469        free(aaa);
01470        return ret;
01471 }
01472 
01473 
01474 /* LFLR */
01475 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
01476 {
01477        size_t bytes;
01478 
01479        if (!cret) return -2;
01480        if (!listing) return -2;
01481        if (*listing) return -2;
01482 
01483        return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
01484 }
01485 
01486 
01487 /* CFLR */
01488 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
01489 {
01490        register int ret;
01491        char aaa[SIZ];
01492 
01493        if (!cret) return -2;
01494        if (!name) return -2;
01495 
01496        sprintf(aaa, "CFLR %s|%d", name, for_real);
01497        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01498        return ret;
01499 }
01500 
01501 
01502 /* KFLR */
01503 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
01504 {
01505        char aaa[SIZ];
01506 
01507        if (!cret) return -1;
01508        if (floornum < 0) return -1;
01509 
01510        sprintf(aaa, "KFLR %d|%d", floornum, for_real);
01511        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01512 }
01513 
01514 
01515 /* EFLR */
01516 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
01517 {
01518        register int ret;
01519        char aaa[SIZ];
01520 
01521        if (!cret) return -2;
01522        if (!floorname) return -2;
01523        if (floornum < 0) return -2;
01524 
01525        sprintf(aaa, "EFLR %d|%s", floornum, floorname);
01526        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01527        return ret;
01528 }
01529 
01530 
01531 /*
01532  * IDEN 
01533  *
01534  * You only need to fill out hostname, the defaults will be used if any of the
01535  * other fields are not set properly.
01536  */
01537 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
01538               int revision, const char *software_name, const char *hostname,
01539               char *cret)
01540 {
01541        register int ret;
01542        char *aaa;
01543 
01544        if (developerid < 0 || clientid < 0 || revision < 0 ||
01545            !software_name) {
01546               developerid = 8;
01547               clientid = 0;
01548               revision = REV_LEVEL - 600;
01549               software_name = "Citadel (libcitadel)";
01550        }
01551        if (!hostname) return -2;
01552 
01553        aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
01554        if (!aaa) return -1;
01555 
01556        sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
01557                      revision, software_name, hostname);
01558        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01559        free(aaa);
01560        return ret;
01561 }
01562 
01563 
01564 /* SEXP */
01565 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
01566               char *cret)
01567 {
01568        register int ret;
01569        char *aaa;
01570 
01571        if (!cret) return -2;
01572        if (!username) return -2;
01573 
01574        aaa = (char *)malloc(strlen(username) + 8);
01575        if (!aaa) return -1;
01576 
01577        if (text) {
01578               sprintf(aaa, "SEXP %s|-", username);
01579               ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
01580                             NULL, NULL, cret);
01581        } else {
01582               sprintf(aaa, "SEXP %s||", username);
01583               ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01584        }
01585        free(aaa);
01586        return ret;
01587 }
01588 
01589 
01590 /* GEXP */
01591 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
01592 {
01593        size_t bytes;
01594 
01595        if (!cret) return -2;
01596        if (!listing) return -2;
01597        if (*listing) return -2;
01598 
01599        return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
01600 }
01601 
01602 
01603 /* DEXP */
01604 /* mode is 0 = enable, 1 = disable, 2 = status */
01605 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
01606 {
01607        char aaa[64];
01608 
01609        if (!cret) return -2;
01610 
01611        sprintf(aaa, "DEXP %d", mode);
01612        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01613 }
01614 
01615 
01616 /* EBIO */
01617 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
01618 {
01619        if (!cret) return -2;
01620        if (!bio) return -2;
01621 
01622        return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
01623                      NULL, NULL, cret);
01624 }
01625 
01626 
01627 /* RBIO */
01628 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
01629 {
01630        register int ret;
01631        size_t bytes;
01632        char *aaa;
01633 
01634        if (!cret) return -2;
01635        if (!username) return -2;
01636        if (!listing) return -2;
01637        if (*listing) return -2;
01638 
01639        aaa = (char *)malloc(strlen(username) + 6);
01640        if (!aaa) return -1;
01641 
01642        sprintf(aaa, "RBIO %s", username);
01643        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
01644        free(aaa);
01645        return ret;
01646 }
01647 
01648 
01649 /* LBIO */
01650 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
01651 {
01652        size_t bytes;
01653 
01654        if (!cret) return -2;
01655        if (!listing) return -2;
01656        if (*listing) return -2;
01657 
01658        return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
01659 }
01660 
01661 
01662 /* STEL */
01663 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
01664 {
01665        char aaa[64];
01666 
01667        if (!cret) return -1;
01668 
01669        sprintf(aaa, "STEL %d", mode ? 1 : 0);
01670        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01671 }
01672 
01673 
01674 /* TERM */
01675 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
01676 {
01677        char aaa[64];
01678 
01679        if (!cret) return -1;
01680 
01681        sprintf(aaa, "TERM %d", sid);
01682        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01683 }
01684 
01685 
01686 /* DOWN */
01687 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
01688 {
01689        if (!cret) return -1;
01690 
01691        return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
01692 }
01693 
01694 
01695 /* SCDN */
01696 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
01697 {
01698        char aaa[16];
01699 
01700        if (!cret) return -1;
01701 
01702        sprintf(aaa, "SCDN %d", mode ? 1 : 0);
01703        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01704 }
01705 
01706 
01707 /* EMSG */
01708 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
01709               char *cret)
01710 {
01711        register int ret;
01712        char *aaa;
01713 
01714        if (!cret) return -2;
01715        if (!text) return -2;
01716        if (!filename) return -2;
01717 
01718        aaa = (char *)malloc(strlen(filename) + 6);
01719        if (!aaa) return -1;
01720 
01721        sprintf(aaa, "EMSG %s", filename);
01722        ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
01723        free(aaa);
01724        return ret;
01725 }
01726 
01727 
01728 /* HCHG */
01729 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
01730 {
01731        register int ret;
01732        char *aaa;
01733 
01734        if (!cret) return -2;
01735        if (!hostname) return -2;
01736 
01737        aaa = (char *)malloc(strlen(hostname) + 6);
01738        if (!aaa) return -1;
01739 
01740        sprintf(aaa, "HCHG %s", hostname);
01741        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01742        free(aaa);
01743        return ret;
01744 }
01745 
01746 
01747 /* RCHG */
01748 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
01749 {
01750        register int ret;
01751        char *aaa;
01752 
01753        if (!cret) return -2;
01754        if (!roomname) return -2;
01755 
01756        aaa = (char *)malloc(strlen(roomname) + 6);
01757        if (!aaa) return -1;
01758 
01759        sprintf(aaa, "RCHG %s", roomname);
01760        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01761        free(aaa);
01762        return ret;
01763 }
01764 
01765 
01766 /* UCHG */
01767 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
01768 {
01769        register int ret;
01770        char *aaa;
01771 
01772        if (!cret) return -2;
01773        if (!username) return -2;
01774 
01775        aaa = (char *)malloc(strlen(username) + 6);
01776        if (!aaa) return -1;
01777 
01778        sprintf(aaa, "UCHG %s", username);
01779        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01780        free(aaa);
01781        return ret;
01782 }
01783 
01784 
01785 /* TIME */
01786 /* This function returns the actual server time reported, or 0 if error */
01787 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
01788 {
01789        register time_t tret;
01790        register int ret;
01791 
01792        ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
01793        if (ret / 100 == 2) {
01794               tret = extract_long(cret, 0);
01795        } else {
01796               tret = 0L;
01797        }
01798        return tret;
01799 }
01800 
01801 
01802 /* AGUP */
01803 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
01804                              struct ctdluser **uret, char *cret)
01805 {
01806        register int ret;
01807        char aaa[SIZ];
01808 
01809        if (!cret) return -2;
01810        if (!uret) return -2;
01811        if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
01812        if (!*uret) return -1;
01813 
01814        sprintf(aaa, "AGUP %s", who);
01815        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01816 
01817        if (ret / 100 == 2) {
01818               extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
01819               extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
01820               uret[0]->flags = extract_int(cret, 2);
01821               uret[0]->timescalled = extract_long(cret, 3);
01822               uret[0]->posted = extract_long(cret, 4);
01823               uret[0]->axlevel = extract_int(cret, 5);
01824               uret[0]->usernum = extract_long(cret, 6);
01825               uret[0]->lastcall = extract_long(cret, 7);
01826               uret[0]->USuserpurge = extract_int(cret, 8);
01827        }
01828        return ret;
01829 }
01830 
01831 
01832 /* ASUP */
01833 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
01834 {
01835        register int ret;
01836        char *aaa;
01837 
01838        if (!cret) return -2;
01839        if (!uret) return -2;
01840 
01841        aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
01842        if (!aaa) return -1;
01843 
01844        sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
01845                      uret->fullname, uret->password, uret->flags,
01846                      uret->timescalled, uret->posted, uret->axlevel,
01847                      uret->usernum, uret->lastcall, uret->USuserpurge);
01848        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01849        free(aaa);
01850        return ret;
01851 }
01852 
01853 
01854 /* GPEX */
01855 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
01856 /* caller must free the struct ExpirePolicy */
01857 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
01858               struct ExpirePolicy **policy, char *cret)
01859 {
01860        static char *proto[] = {
01861               strof(roompolicy),
01862               strof(floorpolicy),
01863               strof(sitepolicy),
01864               strof(mailboxespolicy)
01865        };
01866        char cmd[256];
01867        register int ret;
01868 
01869        if (!cret) return -2;
01870        if (!policy) return -2;
01871        if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
01872        if (!*policy) return -1;
01873        if (which < 0 || which > 3) return -2;
01874        
01875        sprintf(cmd, "GPEX %s", proto[which]);
01876        ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
01877        if (ret / 100 == 2) {
01878               policy[0]->expire_mode = extract_int(cret, 0);
01879               policy[0]->expire_value = extract_int(cret, 1);
01880        }
01881        return ret;
01882 }
01883 
01884 
01885 /* SPEX */
01886 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
01887 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
01888 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
01889               struct ExpirePolicy *policy, char *cret)
01890 {
01891        char aaa[38];
01892        char *whichvals[] = { "room", "floor", "site", "mailboxes" };
01893 
01894        if (!cret) return -2;
01895        if (which < 0 || which > 3) return -2;
01896        if (!policy) return -2;
01897        if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
01898        if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
01899 
01900        sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
01901                      policy->expire_mode, policy->expire_value);
01902        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
01903 }
01904 
01905 
01906 /* CONF GET */
01907 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
01908 {
01909        size_t bytes;
01910 
01911        if (!cret) return -2;
01912        if (!listing) return -2;
01913        if (*listing) return -2;
01914 
01915        return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
01916                      listing, &bytes, cret);
01917 }
01918 
01919 
01920 /* CONF SET */
01921 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
01922 {
01923        if (!cret) return -2;
01924        if (!listing) return -2;
01925 
01926        return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
01927                      NULL, NULL, cret);
01928 }
01929 
01930 
01931 /* CONF GETSYS */
01932 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
01933                      char **listing, char *cret)
01934 {
01935        register int ret;
01936        char *aaa;
01937        size_t bytes;
01938 
01939        if (!cret) return -2;
01940        if (!mimetype) return -2;
01941        if (!listing) return -2;
01942        if (*listing) return -2;
01943 
01944        aaa = malloc(strlen(mimetype) + 13);
01945        if (!aaa) return -1;
01946        sprintf(aaa, "CONF GETSYS|%s", mimetype);
01947        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
01948                      listing, &bytes, cret);
01949     free(aaa);
01950     return ret;
01951 }
01952 
01953 
01954 /* CONF PUTSYS */
01955 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
01956               const char *listing, char *cret)
01957 {
01958     register int ret;
01959        char *aaa;
01960 
01961        if (!cret) return -2;
01962        if (!mimetype) return -2;
01963        if (!listing) return -2;
01964 
01965        aaa = malloc(strlen(mimetype) + 13);
01966        if (!aaa) return -1;
01967        sprintf(aaa, "CONF PUTSYS|%s", mimetype);
01968        ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
01969                      NULL, NULL, cret);
01970     free(aaa);
01971     return ret;
01972 }
01973 
01974 
01975 /* GNET */
01976 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
01977 {
01978        size_t bytes;
01979 
01980        if (!cret) return -2;
01981        if (!listing) return -2;
01982        if (*listing) return -2;
01983 
01984        return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
01985                      listing, &bytes, cret);
01986 }
01987 
01988 
01989 /* SNET */
01990 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
01991 {
01992        if (!cret) return -2;
01993        if (!listing) return -2;
01994 
01995        return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
01996                      NULL, NULL, cret);
01997 }
01998 
01999 
02000 /* REQT */
02001 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
02002 {
02003        char aaa[64];
02004 
02005        if (!cret) return -2;
02006        if (session < 0) return -2;
02007 
02008        sprintf(aaa, "REQT %d", session);
02009        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
02010 }
02011 
02012 
02013 /* SEEN */
02014 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
02015 {
02016        char aaa[27];
02017 
02018        if (!cret) return -2;
02019        if (msgnum < 0) return -2;
02020 
02021        sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
02022        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
02023 }
02024 
02025 
02026 /* STLS */
02027 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
02028 {
02029        int a;
02030        int r;
02031        char buf[SIZ];
02032 
02033 #ifdef HAVE_OPENSSL
02034        SSL *temp_ssl;
02035 
02036        /* New SSL object */
02037        temp_ssl = SSL_new(ssl_ctx);
02038        if (!temp_ssl) {
02039               error_printf("SSL_new failed: %s\n",
02040                             ERR_reason_error_string(ERR_get_error()));
02041               return -2;
02042        }
02043        /* Pointless flag waving */
02044 #if SSLEAY_VERSION_NUMBER >= 0x0922
02045        SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
02046 #endif
02047 
02048        if (!access(EGD_POOL, F_OK))
02049               RAND_egd(EGD_POOL);
02050 
02051        if (!RAND_status()) {
02052               error_printf("PRNG not properly seeded\n");
02053               return -2;
02054        }
02055 
02056        /* Associate network connection with SSL object */
02057        if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
02058               error_printf("SSL_set_fd failed: %s\n",
02059                             ERR_reason_error_string(ERR_get_error()));
02060               return -2;
02061        }
02062 
02063        if (status_hook != NULL)
02064               status_hook("Requesting encryption...\r");
02065 
02066        /* Ready to start SSL/TLS */
02067        /* Old code
02068        CtdlIPC_putline(ipc, "STLS");
02069        CtdlIPC_getline(ipc, buf);
02070        if (buf[0] != '2') {
02071               error_printf("Server can't start TLS: %s\n", buf);
02072               return 0;
02073        }
02074        */
02075        r = CtdlIPCGenericCommand(ipc,
02076                               "STLS", NULL, 0, NULL, NULL, cret);
02077        if (r / 100 != 2) {
02078               error_printf("Server can't start TLS: %s\n", buf);
02079               endtls(temp_ssl);
02080               return r;
02081        }
02082 
02083        /* Do SSL/TLS handshake */
02084        if ((a = SSL_connect(temp_ssl)) < 1) {
02085               error_printf("SSL_connect failed: %s\n",
02086                             ERR_reason_error_string(ERR_get_error()));
02087               endtls(temp_ssl);
02088               return -2;
02089        }
02090        ipc->ssl = temp_ssl;
02091 
02092        if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
02093        {
02094               int bits, alg_bits;
02095 
02096               bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
02097               error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
02098                             SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
02099                             SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
02100                             bits, alg_bits);
02101        }
02102        return r;
02103 #else
02104        return 0;
02105 #endif /* HAVE_OPENSSL */
02106 }
02107 
02108 
02109 #ifdef HAVE_OPENSSL
02110 static void endtls(SSL *ssl)
02111 {
02112        if (ssl) {
02113               SSL_shutdown(ssl);
02114               SSL_free(ssl);
02115        }
02116 }
02117 #endif
02118 
02119 
02120 /* QDIR */
02121 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
02122 {
02123     register int ret;
02124        char *aaa;
02125 
02126        if (!address) return -2;
02127        if (!cret) return -2;
02128 
02129        aaa = (char *)malloc(strlen(address) + 6);
02130        if (!aaa) return -1;
02131 
02132        sprintf(aaa, "QDIR %s", address);
02133        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
02134     free(aaa);
02135     return ret;
02136 }
02137 
02138 
02139 /* IPGM */
02140 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
02141 {
02142        char aaa[30];
02143 
02144        if (!cret) return -2;
02145        sprintf(aaa, "IPGM %d", secret);
02146        return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
02147 }
02148 
02149 
02150 
02151 /*
02152  * Not implemented:
02153  * 
02154  * CHAT
02155  * ETLS
02156  * EXPI
02157  * GTLS
02158  * IGAB
02159  * MSG3
02160  * MSG4
02161  * NDOP
02162  * NETP
02163  * NUOP
02164  * SMTP
02165  */
02166 
02167 
02168 /* ************************************************************************** */
02169 /*          Stuff below this line is not for public consumption           */
02170 /* ************************************************************************** */
02171 
02172 
02173 /* Read a listing from the server up to 000.  Append to dest if it exists */
02174 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
02175 {
02176        size_t length = 0;
02177        size_t linelength;
02178        char *ret = NULL;
02179        char aaa[SIZ];
02180 
02181        ret = dest;
02182        if (ret != NULL) {
02183               length = strlen(ret);
02184        } else {
02185               length = 0;
02186        }
02187 
02188        while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
02189               linelength = strlen(aaa);
02190               ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
02191               if (ret) {
02192                      strcpy(&ret[length], aaa);
02193                      length += linelength;
02194                      strcpy(&ret[length++], "\n");
02195               }
02196        }
02197 
02198        return(ret);
02199 }
02200 
02201 
02202 /* Send a listing to the server; generate the ending 000. */
02203 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
02204 {
02205        char *text;
02206 
02207        text = (char *)malloc(strlen(listing) + 6);
02208        if (text) {
02209               strcpy(text, listing);
02210               while (text[strlen(text) - 1] == '\n')
02211                      text[strlen(text) - 1] = '\0';
02212               strcat(text, "\n000");
02213               CtdlIPC_putline(ipc, text);
02214               free(text);
02215               text = NULL;
02216        } else {
02217               /* Malloc failed but we are committed to send */
02218               /* This may result in extra blanks at the bottom */
02219               CtdlIPC_putline(ipc, text);
02220               CtdlIPC_putline(ipc, "000");
02221        }
02222        return 0;
02223 }
02224 
02225 
02226 /* Partial read of file from server */
02227 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
02228 {
02229        register size_t len = 0;
02230        char aaa[SIZ];
02231 
02232        if (!buf) return 0;
02233        if (!cret) return 0;
02234        if (bytes < 1) return 0;
02235 
02236        CtdlIPC_lock(ipc);
02237        sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
02238        CtdlIPC_putline(ipc, aaa);
02239        CtdlIPC_getline(ipc, aaa);
02240        if (aaa[0] != '6')
02241               strcpy(cret, &aaa[4]);
02242        else {
02243               len = extract_long(&aaa[4], 0);
02244               *buf = (void *)realloc(*buf, (size_t)(offset + len));
02245               if (*buf) {
02246                      /* I know what I'm doing */
02247                      serv_read(ipc, ((char *)(*buf) + offset), len);
02248               } else {
02249                      /* We have to read regardless */
02250                      serv_read(ipc, aaa, len);
02251                      len = 0;
02252               }
02253        }
02254        CtdlIPC_unlock(ipc);
02255        return len;
02256 }
02257 
02258 
02259 /* CLOS */
02260 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
02261 {
02262        register int ret;
02263 
02264        if (!cret) return -2;
02265        if (!ipc->downloading) return -2;
02266 
02267        ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
02268        if (ret / 100 == 2)
02269               ipc->downloading = 0;
02270        return ret;
02271 }
02272 
02273 
02274 /* MSGP */
02275 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
02276        register int ret;
02277        char cmd[SIZ];
02278        
02279        snprintf(cmd, sizeof cmd, "MSGP %s", formats);
02280        ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
02281        return ret;
02282 }
02283 
02284 
02285 
02286 /* READ */
02287 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
02288               void (*progress_gauge_callback)
02289                      (CtdlIPC*, unsigned long, unsigned long),
02290               char *cret)
02291 {
02292        register size_t len;
02293 
02294        if (!cret) return -1;
02295        if (!buf) return -1;
02296        if (*buf) return -1;
02297        if (!ipc->downloading) return -1;
02298 
02299        len = resume;
02300        if (progress_gauge_callback)
02301               progress_gauge_callback(ipc, len, bytes);
02302        while (len < bytes) {
02303               register size_t block;
02304 
02305               block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
02306               if (block == 0) {
02307                      free(*buf);
02308                      return 0;
02309               }
02310               len += block;
02311               if (progress_gauge_callback)
02312                      progress_gauge_callback(ipc, len, bytes);
02313        }
02314        return len;
02315 }
02316 
02317 /* READ - pipelined */
02318 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
02319               size_t resume,
02320               void (*progress_gauge_callback)
02321                      (CtdlIPC*, unsigned long, unsigned long),
02322               char *cret)
02323 {
02324        register size_t len;
02325        register int calls;  /* How many calls in the pipeline */
02326        register int i;             /* iterator */
02327        char aaa[4096];
02328 
02329        if (!cret) return -1;
02330        if (!buf) return -1;
02331        if (*buf) return -1;
02332        if (!ipc->downloading) return -1;
02333 
02334        *buf = (void *)realloc(*buf, bytes - resume);
02335        if (!*buf) return -1;
02336 
02337        len = 0;
02338        CtdlIPC_lock(ipc);
02339        if (progress_gauge_callback)
02340               progress_gauge_callback(ipc, len, bytes);
02341 
02342        /* How many calls will be in the pipeline? */
02343        calls = (bytes - resume) / 4096;
02344        if ((bytes - resume) % 4096) calls++;
02345 
02346        /* Send all requests at once */
02347        for (i = 0; i < calls; i++) {
02348               sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
02349               CtdlIPC_putline(ipc, aaa);
02350        }
02351 
02352        /* Receive all responses at once */
02353        for (i = 0; i < calls; i++) {
02354               CtdlIPC_getline(ipc, aaa);
02355               if (aaa[0] != '6')
02356                      strcpy(cret, &aaa[4]);
02357               else {
02358                      len = extract_long(&aaa[4], 0);
02359                      /* I know what I'm doing */
02360                      serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
02361               }
02362               if (progress_gauge_callback)
02363                      progress_gauge_callback(ipc, i * 4096 + len, bytes);
02364        }
02365        CtdlIPC_unlock(ipc);
02366        return len;
02367 }
02368 
02369 
02370 /* UCLS */
02371 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
02372 {
02373        register int ret;
02374        char cmd[8];
02375 
02376        if (!cret) return -1;
02377        if (!ipc->uploading) return -1;
02378 
02379        sprintf(cmd, "UCLS %d", discard ? 0 : 1);
02380        ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
02381        ipc->uploading = 0;
02382        return ret;
02383 }
02384 
02385 
02386 /* WRIT */
02387 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
02388               void (*progress_gauge_callback)
02389                      (CtdlIPC*, unsigned long, unsigned long),
02390               char *cret)
02391 {
02392        register int ret = -1;
02393        register size_t offset = 0;
02394        size_t bytes;
02395        char aaa[SIZ];
02396        char buf[4096];
02397        FILE *fd = uploadFP;
02398        int ferr;
02399 
02400        if (!cret) return -1;
02401 
02402        fseek(fd, 0L, SEEK_END);
02403        bytes = ftell(fd);
02404        rewind(fd);
02405 
02406        if (progress_gauge_callback)
02407               progress_gauge_callback(ipc, 0, bytes);
02408 
02409        while (offset < bytes) {
02410               register size_t to_write;
02411 
02412               /* Read some data in */
02413               to_write = fread(buf, 1, 4096, fd);
02414               if (!to_write) {
02415                      if (feof(fd) || ferror(fd)) break;
02416               }
02417               sprintf(aaa, "WRIT %d", (int)to_write);
02418               CtdlIPC_putline(ipc, aaa);
02419               CtdlIPC_getline(ipc, aaa);
02420               strcpy(cret, &aaa[4]);
02421               ret = atoi(aaa);
02422               if (aaa[0] == '7') {
02423                      to_write = extract_long(&aaa[4], 0);
02424                      
02425                      serv_write(ipc, buf, to_write);
02426                      offset += to_write;
02427                      if (progress_gauge_callback)
02428                             progress_gauge_callback(ipc, offset, bytes);
02429                      /* Detect short reads and back up if needed */
02430                      /* offset will never be negative anyway */
02431                      fseek(fd, (signed)offset, SEEK_SET);
02432               } else {
02433                      break;
02434               }
02435        }
02436        if (progress_gauge_callback)
02437               progress_gauge_callback(ipc, 1, 1);
02438        ferr = ferror(fd);
02439        fclose(fd);
02440        return (!ferr ? ret : -2);
02441 }
02442 
02443 
02444 /*
02445  * Generic command method.  This method should handle any server command
02446  * except for CHAT.  It takes the following arguments:
02447  *
02448  * ipc               The server to speak with
02449  * command           Preformatted command to send to server
02450  * to_send           A text or binary file to send to server
02451  *                   (only sent if server requests it)
02452  * bytes_to_send     The number of bytes in to_send (required if
02453  *                   sending binary, optional if sending listing)
02454  * to_receive        Pointer to a NULL pointer, if the server
02455  *                   sends text or binary we will allocate memory
02456  *                   for the file and stuff it here
02457  * bytes_to_receive  If a file is received, we will store its
02458  *                   byte count here
02459  * proto_response    The protocol response.  Caller must provide
02460  *                   this buffer and ensure that it is at least
02461  *                   128 bytes in length.
02462  *
02463  * This function returns a number equal to the protocol response number,
02464  * -1 if an internal error occurred, -2 if caller provided bad values,
02465  * or 0 - the protocol response number if bad values were found during
02466  * the protocol exchange.
02467  * It stores the protocol response string (minus the number) in 
02468  * protocol_response as described above.  Some commands send additional
02469  * data in this string.
02470  */
02471 int CtdlIPCGenericCommand(CtdlIPC *ipc,
02472               const char *command, const char *to_send,
02473               size_t bytes_to_send, char **to_receive, 
02474               size_t *bytes_to_receive, char *proto_response)
02475 {
02476        char buf[SIZ];
02477        register int ret;
02478 
02479        if (!command) return -2;
02480        if (!proto_response) return -2;
02481 
02482        CtdlIPC_lock(ipc);
02483        CtdlIPC_putline(ipc, command);
02484        while (1) {
02485               CtdlIPC_getline(ipc, proto_response);
02486               if (proto_response[3] == '*')
02487                      instant_msgs = 1;
02488               ret = atoi(proto_response);
02489               strcpy(proto_response, &proto_response[4]);
02490               switch (ret / 100) {
02491               default:                    /* Unknown, punt */
02492               case 2:                            /* OK */
02493               case 3:                            /* MORE_DATA */
02494               case 5:                            /* ERROR */
02495                      /* Don't need to do anything */
02496                      break;
02497               case 1:                            /* LISTING_FOLLOWS */
02498                      if (to_receive && !*to_receive && bytes_to_receive) {
02499                             *to_receive = CtdlIPCReadListing(ipc, NULL);
02500                      } else { /* Drain */
02501                             while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
02502                             ret = -ret;
02503                      }
02504                      break;
02505               case 4:                            /* SEND_LISTING */
02506                      if (to_send) {
02507                             CtdlIPCSendListing(ipc, to_send);
02508                      } else {
02509                             /* No listing given, fake it */
02510                             CtdlIPC_putline(ipc, "000");
02511                             ret = -ret;
02512                      }
02513                      break;
02514               case 6:                            /* BINARY_FOLLOWS */
02515                      if (to_receive && !*to_receive && bytes_to_receive) {
02516                             *bytes_to_receive =
02517                                    extract_long(proto_response, 0);
02518                             *to_receive = (char *)
02519                                    malloc((size_t)*bytes_to_receive);
02520                             if (!*to_receive) {
02521                                    ret = -1;
02522                             } else {
02523                                    serv_read(ipc, *to_receive,
02524                                                  *bytes_to_receive);
02525                             }
02526                      } else {
02527                             /* Drain */
02528                             size_t drain;
02529 
02530                             drain = extract_long(proto_response, 0);
02531                             while (drain > SIZ) {
02532                                    serv_read(ipc, buf, SIZ);
02533                                    drain -= SIZ;
02534                             }
02535                             serv_read(ipc, buf, drain);
02536                             ret = -ret;
02537                      }
02538                      break;
02539               case 7:                            /* SEND_BINARY */
02540                      if (to_send && bytes_to_send) {
02541                             serv_write(ipc, to_send, bytes_to_send);
02542                      } else if (bytes_to_send) {
02543                             /* Fake it, send nulls */
02544                             size_t fake;
02545 
02546                             fake = bytes_to_send;
02547                             memset(buf, '\0', SIZ);
02548                             while (fake > SIZ) {
02549                                    serv_write(ipc, buf, SIZ);
02550                                    fake -= SIZ;
02551                             }
02552                             serv_write(ipc, buf, fake);
02553                             ret = -ret;
02554                      } /* else who knows?  DANGER WILL ROBINSON */
02555                      break;
02556               case 8:                            /* START_CHAT_MODE */
02557                      if (!strncasecmp(command, "CHAT", 4)) {
02558                             /* Don't call chatmode with generic! */
02559                             CtdlIPC_putline(ipc, "/quit");
02560                             ret = -ret;
02561                      } else {
02562                             /* In this mode we send then receive listing */
02563                             if (to_send) {
02564                                    CtdlIPCSendListing(ipc, to_send);
02565                             } else {
02566                                    /* No listing given, fake it */
02567                                    CtdlIPC_putline(ipc, "000");
02568                                    ret = -ret;
02569                             }
02570                             if (to_receive && !*to_receive
02571                                           && bytes_to_receive) {
02572                                    *to_receive = CtdlIPCReadListing(ipc, NULL);
02573                             } else { /* Drain */
02574                                    while (CtdlIPC_getline(ipc, buf),
02575                                                  strcmp(buf, "000")) ;
02576                                    ret = -ret;
02577                             }
02578                      }
02579                      break;
02580               case 9:                            /* ASYNC_MSG */
02581                      /* CtdlIPCDoAsync(ret, proto_response); */
02582                      free(CtdlIPCReadListing(ipc, NULL));      /* STUB FIXME */
02583                      break;
02584               }
02585               if (ret / 100 != 9)
02586                      break;
02587        }
02588        CtdlIPC_unlock(ipc);
02589        return ret;
02590 }
02591 
02592 
02593 /*
02594  * Connect to a Citadel on a remote host using a TCP/IP socket
02595  */
02596 static int tcp_connectsock(char *host, char *service)
02597 {
02598        struct in6_addr serveraddr;
02599        struct addrinfo hints;
02600        struct addrinfo *res = NULL;
02601        struct addrinfo *ai = NULL;
02602        int rc = (-1);
02603        int sock = (-1);
02604 
02605        if ((host == NULL) || IsEmptyStr(host)) {
02606               service = DEFAULT_HOST ;
02607        }
02608        if ((service == NULL) || IsEmptyStr(service)) {
02609               service = DEFAULT_PORT ;
02610        }
02611 
02612        memset(&hints, 0x00, sizeof(hints));
02613        hints.ai_flags = AI_NUMERICSERV;
02614        hints.ai_family = AF_UNSPEC;
02615        hints.ai_socktype = SOCK_STREAM;
02616 
02617        /*
02618         * Handle numeric IPv4 and IPv6 addresses
02619         */
02620        rc = inet_pton(AF_INET, host, &serveraddr);
02621        if (rc == 1) {                                          /* dotted quad */
02622               hints.ai_family = AF_INET;
02623               hints.ai_flags |= AI_NUMERICHOST;
02624        }
02625        else {
02626               rc = inet_pton(AF_INET6, host, &serveraddr);
02627               if (rc == 1) {                                   /* IPv6 address */
02628                      hints.ai_family = AF_INET6;
02629                      hints.ai_flags |= AI_NUMERICHOST;
02630               }
02631        }
02632 
02633        /* Begin the connection process */
02634 
02635        rc = getaddrinfo(host, service, &hints, &res);
02636        if (rc != 0) {
02637               return(-1);
02638        }
02639 
02640        /*
02641         * Try all available addresses until we connect to one or until we run out.
02642         */
02643        for (ai = res; ai != NULL; ai = ai->ai_next) {
02644               sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
02645               if (sock < 0) return(-1);
02646 
02647               rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
02648               if (rc >= 0) {
02649                      return(sock);        /* Connected! */
02650               }
02651               else {
02652                      close(sock);         /* Failed.  Close the socket to avoid fd leak! */
02653               }
02654        }
02655 
02656        return(-1);
02657 }
02658 
02659 
02660 
02661 
02662 
02663 /*
02664  * Connect to a Citadel on the local host using a unix domain socket
02665  */
02666 static int uds_connectsock(int *isLocal, char *sockpath)
02667 {
02668        struct sockaddr_un addr;
02669        int s;
02670 
02671        memset(&addr, 0, sizeof(addr));
02672        addr.sun_family = AF_UNIX;
02673        safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
02674 
02675        s = socket(AF_UNIX, SOCK_STREAM, 0);
02676        if (s < 0) {
02677               return -1;
02678        }
02679 
02680        if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
02681               close(s);
02682               return -1;
02683        }
02684 
02685        *isLocal = 1;
02686        return s;
02687 }
02688 
02689 
02690 /*
02691  * input binary data from socket
02692  */
02693 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
02694 {
02695        unsigned int len, rlen;
02696 
02697 #if defined(HAVE_OPENSSL)
02698        if (ipc->ssl) {
02699               serv_read_ssl(ipc, buf, bytes);
02700               return;
02701        }
02702 #endif
02703        len = 0;
02704        while (len < bytes) {
02705               rlen = read(ipc->sock, &buf[len], bytes - len);
02706               if (rlen < 1) {
02707                      connection_died(ipc, 0);
02708                      return;
02709               }
02710               len += rlen;
02711        }
02712 }
02713 
02714 
02715 /*
02716  * send binary to server
02717  */
02718 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
02719 {
02720        unsigned int bytes_written = 0;
02721        int retval;
02722 
02723 #if defined(HAVE_OPENSSL)
02724        if (ipc->ssl) {
02725               serv_write_ssl(ipc, buf, nbytes);
02726               return;
02727        }
02728 #endif
02729        while (bytes_written < nbytes) {
02730               retval = write(ipc->sock, &buf[bytes_written],
02731                             nbytes - bytes_written);
02732               if (retval < 1) {
02733                      connection_died(ipc, 0);
02734                      return;
02735               }
02736               bytes_written += retval;
02737        }
02738 }
02739 
02740 
02741 #ifdef HAVE_OPENSSL
02742 /*
02743  * input binary data from encrypted connection
02744  */
02745 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
02746 {
02747        int len, rlen;
02748        char junk[1];
02749 
02750        len = 0;
02751        while (len < bytes) {
02752               if (SSL_want_read(ipc->ssl)) {
02753                      if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
02754                             error_printf("SSL_write in serv_read:\n");
02755                             ERR_print_errors_fp(stderr);
02756                      }
02757               }
02758               rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
02759               if (rlen < 1) {
02760                      long errval;
02761 
02762                      errval = SSL_get_error(ipc->ssl, rlen);
02763                      if (errval == SSL_ERROR_WANT_READ ||
02764                                    errval == SSL_ERROR_WANT_WRITE) {
02765                             sleep(1);
02766                             continue;
02767                      }
02768 /***
02769  Not sure why we'd want to handle these error codes any differently,
02770  but this definitely isn't the way to handle them.  Someone must have
02771  naively assumed that we could fall back to unencrypted communications,
02772  but all it does is just recursively blow the stack.
02773                      if (errval == SSL_ERROR_ZERO_RETURN ||
02774                                    errval == SSL_ERROR_SSL) {
02775                             serv_read(ipc, &buf[len], bytes - len);
02776                             return;
02777                      }
02778  ***/
02779                      error_printf("SSL_read in serv_read: %s\n",
02780                                    ERR_reason_error_string(ERR_peek_error()));
02781                      connection_died(ipc, 1);
02782                      return;
02783               }
02784               len += rlen;
02785        }
02786 }
02787 
02788 
02789 /*
02790  * send binary to server encrypted
02791  */
02792 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
02793 {
02794        unsigned int bytes_written = 0;
02795        int retval;
02796        char junk[1];
02797 
02798        while (bytes_written < nbytes) {
02799               if (SSL_want_write(ipc->ssl)) {
02800                      if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
02801                             error_printf("SSL_read in serv_write:\n");
02802                             ERR_print_errors_fp(stderr);
02803                      }
02804               }
02805               retval = SSL_write(ipc->ssl, &buf[bytes_written],
02806                             nbytes - bytes_written);
02807               if (retval < 1) {
02808                      long errval;
02809 
02810                      errval = SSL_get_error(ipc->ssl, retval);
02811                      if (errval == SSL_ERROR_WANT_READ ||
02812                                    errval == SSL_ERROR_WANT_WRITE) {
02813                             sleep(1);
02814                             continue;
02815                      }
02816                      if (errval == SSL_ERROR_ZERO_RETURN ||
02817                                    errval == SSL_ERROR_SSL) {
02818                             serv_write(ipc, &buf[bytes_written],
02819                                           nbytes - bytes_written);
02820                             return;
02821                      }
02822                      error_printf("SSL_write in serv_write: %s\n",
02823                                    ERR_reason_error_string(ERR_peek_error()));
02824                      connection_died(ipc, 1);
02825                      return;
02826               }
02827               bytes_written += retval;
02828        }
02829 }
02830 
02831 
02832 #ifdef THREADED_CLIENT
02833 static void ssl_lock(int mode, int n, const char *file, int line)
02834 {
02835        if (mode & CRYPTO_LOCK)
02836               pthread_mutex_lock(Critters[n]);
02837        else
02838               pthread_mutex_unlock(Critters[n]);
02839 }
02840 #endif /* THREADED_CLIENT */
02841 
02842 
02843 static void CtdlIPC_init_OpenSSL(void)
02844 {
02845        int a;
02846        const SSL_METHOD *ssl_method;
02847        DH *dh;
02848        
02849        /* already done init */
02850        if (ssl_ctx) {
02851               return;
02852        }
02853 
02854        /* Get started */
02855        a = 0;
02856        ssl_ctx = NULL;
02857        dh = NULL;
02858        SSL_load_error_strings();
02859        SSLeay_add_ssl_algorithms();
02860 
02861        /* Set up the SSL context in which we will oeprate */
02862        ssl_method = SSLv23_client_method();
02863        ssl_ctx = SSL_CTX_new(ssl_method);
02864        if (!ssl_ctx) {
02865               error_printf("SSL_CTX_new failed: %s\n",
02866                             ERR_reason_error_string(ERR_get_error()));
02867               return;
02868        }
02869        /* Any reasonable cipher we can get */
02870        if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
02871               error_printf("No ciphers available for encryption\n");
02872               return;
02873        }
02874        SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
02875        
02876        /* Load DH parameters into the context */
02877        dh = DH_new();
02878        if (!dh) {
02879               error_printf("Can't allocate a DH object: %s\n",
02880                             ERR_reason_error_string(ERR_get_error()));
02881               return;
02882        }
02883        if (!(BN_hex2bn(&(dh->p), DH_P))) {
02884               error_printf("Can't assign DH_P: %s\n",
02885                             ERR_reason_error_string(ERR_get_error()));
02886               DH_free(dh);
02887               return;
02888        }
02889        if (!(BN_hex2bn(&(dh->g), DH_G))) {
02890               error_printf("Can't assign DH_G: %s\n",
02891                             ERR_reason_error_string(ERR_get_error()));
02892               DH_free(dh);
02893               return;
02894        }
02895        dh->length = DH_L;
02896        SSL_CTX_set_tmp_dh(ssl_ctx, dh);
02897        DH_free(dh);
02898 
02899 #ifdef THREADED_CLIENT
02900        /* OpenSSL requires callbacks for threaded clients */
02901        CRYPTO_set_locking_callback(ssl_lock);
02902        CRYPTO_set_id_callback(id_callback);
02903 
02904        /* OpenSSL requires us to do semaphores for threaded clients */
02905        Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
02906        if (!Critters) {
02907               perror("malloc failed");
02908               exit(1);
02909        } else {
02910               for (a = 0; a < CRYPTO_num_locks(); a++) {
02911                      Critters[a] = malloc(sizeof (pthread_mutex_t));
02912                      if (!Critters[a]) {
02913                             perror("malloc failed");
02914                             exit(1);
02915                      }
02916                      pthread_mutex_init(Critters[a], NULL);
02917               }
02918        }
02919 #endif /* THREADED_CLIENT */       
02920 }
02921 
02922 
02923 
02924 #ifdef THREADED_CLIENT
02925 static unsigned long id_callback(void) {
02926        return (unsigned long)pthread_self();
02927 }
02928 #endif /* THREADED_CLIENT */
02929 #endif /* HAVE_OPENSSL */
02930 
02931 
02932 int
02933 ReadNetworkChunk(CtdlIPC* ipc)
02934 {
02935        fd_set read_fd;
02936 /*     int tries;*/
02937        int ret = 0;
02938        int err = 0;
02939        struct timeval tv;
02940        size_t n;
02941 
02942        tv.tv_sec = 1;
02943        tv.tv_usec = 1000;
02944        /*tries = 0; */
02945        n = 0;
02946        while (1)
02947        {
02948               errno=0;
02949               FD_ZERO(&read_fd);
02950               FD_SET(ipc->sock, &read_fd);
02951               ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
02952               
02953 //            fprintf(stderr, "\nselect failed: %d %d %s\n", ret,  err, strerror(err));
02954               
02955               if (ret > 0) {
02956                      
02957                      *(ipc->BufPtr) = '\0';
02958 //                   n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
02959                      n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
02960                      if (n > 0) {
02961                             ipc->BufPtr[n]='\0';
02962                             ipc->BufUsed += n;
02963                             return n;
02964                      }
02965                      else 
02966                             return n;
02967               }
02968               else if (ret < 0) {
02969                      if (!(errno == EINTR || errno == EAGAIN))
02970                             error_printf( "\nselect failed: %d %s\n", err, strerror(err));
02971                      return -1;
02972               }/*
02973               else {
02974                      tries ++;
02975                      if (tries >= 10)
02976                      n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
02977                      if (n > 0) {
02978                             ipc->BufPtr[n]='\0';
02979                             ipc->BufUsed += n;
02980                             return n;
02981                      }
02982                      else {
02983                             connection_died(ipc, 0);
02984                             return -1;
02985                      }
02986                      }*/
02987        }
02988 }
02989 
02990 /*
02991  * input string from socket - implemented in terms of serv_read()
02992  */
02993 #ifdef CHUNKED_READ
02994 
02995 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
02996 {
02997        int i, ntries;
02998        char *aptr, *bptr, *aeptr, *beptr;
02999 
03000 //     error_printf("---\n");
03001 
03002        beptr = buf + SIZ;
03003 #if defined(HAVE_OPENSSL)
03004        if (ipc->ssl) {
03005               
03006               /* Read one character at a time. */
03007               for (i = 0;; i++) {
03008                      serv_read(ipc, &buf[i], 1);
03009                      if (buf[i] == '\n' || i == (SIZ-1))
03010                             break;
03011               }
03012               
03013               /* If we got a long line, discard characters until the newline. */
03014               if (i == (SIZ-1))
03015                      while (buf[i] != '\n')
03016                             serv_read(ipc, &buf[i], 1);
03017               
03018               /* Strip the trailing newline (and carriage return, if present) */
03019               if (i>=0 && buf[i] == 10) buf[i--] = 0;
03020               if (i>=0 && buf[i] == 13) buf[i--] = 0;
03021        }
03022        else
03023 #endif
03024        {
03025               if (ipc->Buf == NULL)
03026               {
03027                      ipc->BufSize = SIZ;
03028                      ipc->Buf = (char*) malloc(ipc->BufSize + 10);
03029                      *(ipc->Buf) = '\0';
03030                      ipc->BufPtr = ipc->Buf;
03031               }
03032 
03033               ntries = 0;
03034 //            while ((ipc->BufUsed == 0)||(ntries++ > 10))
03035               if (ipc->BufUsed == 0)
03036                      ReadNetworkChunk(ipc);
03037 
03039               bptr = buf;
03040 
03041               while (1)
03042               {
03043                      aptr = ipc->BufPtr;
03044                      aeptr = ipc->Buf + ipc->BufSize;
03045                      while ((aptr < aeptr) && 
03046                             (bptr < beptr) &&
03047                             (*aptr != '\0') && 
03048                             (*aptr != '\n'))
03049                             *(bptr++) = *(aptr++);
03050                      if ((*aptr == '\n') && (aptr < aeptr))
03051                      {
03052                             /* Terminate it right, remove the line breaks */
03053                             while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
03054                                    aptr ++;
03055                             while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
03056                                    aptr ++;
03057                             *(bptr++) = '\0';
03058 //                          fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
03059                             if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
03060                                    *(--bptr) = '\0';
03061                             
03062                             /* is there more in the buffer we need to read later? */
03063                             if (ipc->Buf + ipc->BufUsed > aptr)
03064                             {
03065                                    ipc->BufPtr = aptr;
03066                             }
03067                             else
03068                             {
03069                                    ipc->BufUsed = 0;
03070                                    ipc->BufPtr = ipc->Buf;
03071                             }
03072 //                          error_printf("----bla6\n");
03073                             return;
03074                             
03075                      }/* should we move our read stuf to the bufferstart so we have more space at the end? */
03076                      else if ((ipc->BufPtr != ipc->Buf) && 
03077                              (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
03078                      {
03079                             size_t NewBufSize = ipc->BufSize * 2;
03080                             int delta = (ipc->BufPtr - ipc->Buf);
03081                             char *NewBuf;
03082 
03083                             /* if the line would end after our buffer, we should use a bigger buffer. */
03084                             NewBuf = (char *)malloc (NewBufSize + 10);
03085                             memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
03086                             free(ipc->Buf);
03087                             ipc->Buf = ipc->BufPtr = NewBuf;
03088                             ipc->BufUsed -= delta;
03089                             ipc->BufSize = NewBufSize;
03090                      }
03091                      if (ReadNetworkChunk(ipc) <0)
03092                      {
03093 //                          error_printf("----bla\n");
03094                             return;
03095                      }
03096               }
03098        }
03099 //     error_printf("----bla1\n");
03100 }
03101 
03102 #else  /* CHUNKED_READ */
03103 
03104 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
03105 {
03106        int i;
03107 
03108        /* Read one character at a time. */
03109        for (i = 0;; i++) {
03110               serv_read(ipc, &buf[i], 1);
03111               if (buf[i] == '\n' || i == (SIZ-1))
03112                      break;
03113        }
03114 
03115        /* If we got a long line, discard characters until the newline. */
03116        if (i == (SIZ-1))
03117               while (buf[i] != '\n')
03118                      serv_read(ipc, &buf[i], 1);
03119 
03120        /* Strip the trailing newline (and carriage return, if present) */
03121        if (i>=0 && buf[i] == 10) buf[i--] = 0;
03122        if (i>=0 && buf[i] == 13) buf[i--] = 0;
03123 }
03124 
03125 
03126 #endif /* CHUNKED_READ */
03127 
03128 
03129 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
03130 {
03131        CtdlIPC_getline(ipc, buf);
03132 }
03133 
03134 /*
03135  * send line to server - implemented in terms of serv_write()
03136  */
03137 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
03138 {
03139        char *cmd = NULL;
03140        int len;
03141 
03142        len = strlen(buf);
03143        cmd = malloc(len + 2);
03144        if (!cmd) {
03145               /* This requires no extra memory */
03146               serv_write(ipc, buf, len);
03147               serv_write(ipc, "\n", 1);
03148        } else {
03149               /* This is network-optimized */
03150               strncpy(cmd, buf, len);
03151               strcpy(cmd + len, "\n");
03152               serv_write(ipc, cmd, len + 1);
03153               free(cmd);
03154        }
03155 
03156        ipc->last_command_sent = time(NULL);
03157 }
03158 
03159 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
03160 {
03161        CtdlIPC_putline(ipc, buf);
03162 }
03163 
03164 
03165 /*
03166  * attach to server
03167  */
03168 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
03169 {
03170        int a;
03171        char cithost[SIZ];
03172        char citport[SIZ];
03173        char sockpath[SIZ];
03174        CtdlIPC* ipc;
03175 
03176        ipc = ialloc(CtdlIPC);
03177        if (!ipc) {
03178               return 0;
03179        }
03180 #if defined(HAVE_OPENSSL)
03181        ipc->ssl = NULL;
03182        CtdlIPC_init_OpenSSL();
03183 #endif
03184 #if defined(HAVE_PTHREAD_H)
03185        pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
03186 #endif
03187        ipc->sock = -1;                    /* Not connected */
03188        ipc->isLocal = 0;           /* Not local, of course! */
03189        ipc->downloading = 0;
03190        ipc->uploading = 0;
03191        ipc->last_command_sent = 0L;
03192        ipc->network_status_cb = NULL;
03193        ipc->Buf = NULL;
03194        ipc->BufUsed = 0;
03195        ipc->BufPtr = NULL;
03196 
03197        strcpy(cithost, DEFAULT_HOST);     /* default host */
03198        strcpy(citport, DEFAULT_PORT);     /* default port */
03199 
03200        /* Allow caller to supply our values (Windows) */
03201        if (hostbuf && strlen(hostbuf) > 0)
03202               strcpy(cithost, hostbuf);
03203        if (portbuf && strlen(portbuf) > 0)
03204               strcpy(citport, portbuf);
03205 
03206        /* Read host/port from command line if present */
03207        for (a = 0; a < argc; ++a) {
03208               if (a == 0) {
03209                      /* do nothing */
03210               } else if (a == 1) {
03211                      strcpy(cithost, argv[a]);
03212               } else if (a == 2) {
03213                      strcpy(citport, argv[a]);
03214               } else {
03215                      error_printf("%s: usage: ",argv[0]);
03216                      error_printf("%s [host] [port] ",argv[0]);
03217                      ifree(ipc);
03218                      errno = EINVAL;
03219                      return 0;
03220               }
03221        }
03222 
03223        if ((!strcmp(cithost, "localhost"))
03224           || (!strcmp(cithost, "127.0.0.1"))) {
03225               ipc->isLocal = 1;
03226        }
03227 
03228        /* If we're using a unix domain socket we can do a bunch of stuff */
03229        if (!strcmp(cithost, UDS)) {
03230               if (!strcasecmp(citport, DEFAULT_PORT)) {
03231                      snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
03232               }
03233               else {
03234                      snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
03235               }
03236               ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
03237               if (ipc->sock == -1) {
03238                      ifree(ipc);
03239                      return 0;
03240               }
03241               if (hostbuf != NULL) strcpy(hostbuf, cithost);
03242               if (portbuf != NULL) strcpy(portbuf, sockpath);
03243               strcpy(ipc->ip_hostname, "");
03244               strcpy(ipc->ip_address, "");
03245               return ipc;
03246        }
03247 
03248        ipc->sock = tcp_connectsock(cithost, citport);
03249        if (ipc->sock == -1) {
03250               ifree(ipc);
03251               return 0;
03252        }
03253 
03254 
03255        /* Learn the actual network identity of the host to which we are connected */
03256 
03257        struct sockaddr_in6 clientaddr;
03258        unsigned int addrlen = sizeof(clientaddr);
03259 
03260        ipc->ip_hostname[0] = 0;
03261        ipc->ip_address[0] = 0;
03262 
03263        getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
03264        getnameinfo((struct sockaddr *)&clientaddr, addrlen,
03265               ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
03266        );
03267        getnameinfo((struct sockaddr *)&clientaddr, addrlen,
03268               ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
03269        );
03270 
03271        /* stuff other things elsewhere */
03272 
03273        if (hostbuf != NULL) strcpy(hostbuf, cithost);
03274        if (portbuf != NULL) strcpy(portbuf, citport);
03275        return ipc;
03276 }
03277 
03278 
03279 /*
03280  * Disconnect and delete the IPC class (destructor)
03281  */
03282 void CtdlIPC_delete(CtdlIPC* ipc)
03283 {
03284 #ifdef HAVE_OPENSSL
03285        if (ipc->ssl) {
03286               SSL_shutdown(ipc->ssl);
03287               SSL_free(ipc->ssl);
03288               ipc->ssl = NULL;
03289        }
03290 #endif
03291        if (ipc->sock > -1) {
03292               shutdown(ipc->sock, 2);     /* Close it up */
03293               ipc->sock = -1;
03294        }
03295        if (ipc->Buf != NULL)
03296               free (ipc->Buf);
03297        ipc->Buf = NULL;
03298        ipc->BufPtr = NULL;
03299        ifree(ipc);
03300 }
03301 
03302 
03303 /*
03304  * Disconnect and delete the IPC class (destructor)
03305  * Also NULLs out the pointer
03306  */
03307 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
03308 {
03309        CtdlIPC_delete(*pipc);
03310        *pipc = NULL;
03311 }
03312 
03313 
03314 /*
03315  * return the file descriptor of the server socket so we can select() on it.
03316  *
03317  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
03318  * rewritten...
03319  */
03320 int CtdlIPC_getsockfd(CtdlIPC* ipc)
03321 {
03322        return ipc->sock;
03323 }
03324 
03325 
03326 /*
03327  * return one character
03328  *
03329  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
03330  * rewritten...
03331  */
03332 char CtdlIPC_get(CtdlIPC* ipc)
03333 {
03334        char buf[2];
03335        char ch;
03336 
03337        serv_read(ipc, buf, 1);
03338        ch = (int) buf[0];
03339 
03340        return (ch);
03341 }