* CtdlIPCGetMessages(): Fix message read failing on first attempt to read
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #define UDS                     "_UDS_"
4 #define DEFAULT_HOST            UDS
5 #define DEFAULT_PORT            "citadel"
6
7 #include "sysdep.h"
8 #if TIME_WITH_SYS_TIME
9 # include <sys/time.h>
10 # include <time.h>
11 #else
12 # if HAVE_SYS_TIME_H
13 #  include <sys/time.h>
14 # else
15 #  include <time.h>
16 # endif
17 #endif
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <string.h>
22 #ifdef HAVE_MALLOC_H
23 #include <malloc.h>
24 #endif
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <sys/un.h>
32 #include <errno.h>
33 #ifdef THREADED_CLIENT
34 #include <pthread.h>
35 #endif
36 #include "citadel.h"
37 #include "citadel_ipc.h"
38 #include "citadel_decls.h"
39 #include "tools.h"
40
41 #ifdef THREADED_CLIENT
42 pthread_mutex_t rwlock;
43 #endif
44
45 #ifdef HAVE_OPENSSL
46 static SSL_CTX *ssl_ctx;
47 char arg_encrypt;
48 char rc_encrypt;
49 #ifdef THREADED_CLIENT
50 pthread_mutex_t **Critters;                     /* Things that need locking */
51 #endif /* THREADED_CLIENT */
52
53 #endif /* HAVE_OPENSSL */
54
55 #ifndef INADDR_NONE
56 #define INADDR_NONE 0xffffffff
57 #endif
58
59 static void (*status_hook)(char *s) = NULL;
60
61 void setCryptoStatusHook(void (*hook)(char *s)) {
62         status_hook = hook;
63 }
64
65 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
66         ipc->network_status_cb = hook;
67 }
68
69
70 char express_msgs = 0;
71
72
73 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
74 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
75 #ifdef HAVE_OPENSSL
76 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
77 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
78 static void ssl_lock(int mode, int n, const char *file, int line);
79 static void endtls(SSL *ssl);
80 #ifdef THREADED_CLIENT
81 static unsigned long id_callback(void);
82 #endif /* THREADED_CLIENT */
83 #endif /* HAVE_OPENSSL */
84
85
86 /*
87  * Does nothing.  The server should always return 200.
88  */
89 int CtdlIPCNoop(CtdlIPC *ipc)
90 {
91         char aaa[128];
92
93         return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
94 }
95
96
97 /*
98  * Does nothing interesting.  The server should always return 200
99  * along with your string.
100  */
101 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
102 {
103         register int ret;
104         char *aaa;
105         
106         if (!arg) return -2;
107         if (!cret) return -2;
108
109         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110         if (!aaa) return -1;
111
112         sprintf(aaa, "ECHO %s", arg);
113         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
114         free(aaa);
115         return ret;
116 }
117
118
119 /*
120  * Asks the server to close the connecction.
121  * Should always return 200.
122  */
123 int CtdlIPCQuit(CtdlIPC *ipc)
124 {
125         register int ret;
126         char aaa[128];
127
128         CtdlIPC_lock(ipc);
129         CtdlIPC_putline(ipc, "QUIT");
130         CtdlIPC_getline(ipc, aaa);
131         ret = atoi(aaa);
132         CtdlIPC_unlock(ipc);
133         return ret;
134 }
135
136
137 /*
138  * Asks the server to logout.  Should always return 200, even if no user
139  * was logged in.  The user will not be logged in after this!
140  */
141 int CtdlIPCLogout(CtdlIPC *ipc)
142 {
143         register int ret;
144         char aaa[128];
145
146         CtdlIPC_lock(ipc);
147         CtdlIPC_putline(ipc, "LOUT");
148         CtdlIPC_getline(ipc, aaa);
149         ret = atoi(aaa);
150         CtdlIPC_unlock(ipc);
151         return ret;
152 }
153
154
155 /*
156  * First stage of authentication - pass the username.  Returns 300 if the
157  * username is able to log in, with the username correctly spelled in cret.
158  * Returns various 500 error codes if the user doesn't exist, etc.
159  */
160 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
161 {
162         register int ret;
163         char *aaa;
164
165         if (!username) return -2;
166         if (!cret) return -2;
167
168         aaa = (char *)malloc((size_t)(strlen(username) + 6));
169         if (!aaa) return -1;
170
171         sprintf(aaa, "USER %s", username);
172         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
173         free(aaa);
174         return ret;
175 }
176
177
178 /*
179  * Second stage of authentication - provide password.  The server returns
180  * 200 and several arguments in cret relating to the user's account.
181  */
182 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
183 {
184         register int ret;
185         char *aaa;
186
187         if (!passwd) return -2;
188         if (!cret) return -2;
189
190         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
191         if (!aaa) return -1;
192
193         sprintf(aaa, "PASS %s", passwd);
194         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
195         free(aaa);
196         return ret;
197 }
198
199
200 /*
201  * Second stage of authentication - provide password.  The server returns
202  * 200 and several arguments in cret relating to the user's account.
203  */
204 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
205 {
206         register int ret;
207         char *aaa;
208
209         if (!response) return -2;
210         if (!cret) return -2;
211
212         aaa = (char *)malloc((size_t)(strlen(response) + 6));
213         if (!aaa) return -1;
214
215         sprintf(aaa, "PAS2 %s", response);
216         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
217         free(aaa);
218         return ret;
219 }
220
221
222 /*
223  * Create a new user.  This returns 200 plus the same arguments as TryPassword
224  * if selfservice is nonzero, unless there was a problem creating the account.
225  * If selfservice is zero, creates a new user but does not log out the existing
226  * user - intended for use by system administrators to create accounts on
227  * behalf of other users.
228  */
229 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
230 {
231         register int ret;
232         char *aaa;
233
234         if (!username) return -2;
235         if (!cret) return -2;
236
237         aaa = (char *)malloc((size_t)(strlen(username) + 6));
238         if (!aaa) return -1;
239
240         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
241         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
242         free(aaa);
243         return ret;
244 }
245
246
247 /*
248  * Changes the user's password.  Returns 200 if changed, errors otherwise.
249  */
250 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
251 {
252         register int ret;
253         char *aaa;
254
255         if (!passwd) return -2;
256         if (!cret) return -2;
257
258         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
259         if (!aaa) return -1;
260
261         sprintf(aaa, "SETP %s", passwd);
262         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
263         free(aaa);
264         return ret;
265 }
266
267
268 /* LKRN */
269 /* Caller must free the march list */
270 /* Room types are defined in enum RoomList; keep these in sync! */
271 /* floor is -1 for all, or floornum */
272 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
273 {
274         register int ret;
275         struct march *march = NULL;
276         static char *proto[] =
277                 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
278         char aaa[SIZ];
279         char *bbb = NULL;
280         size_t bbbsize;
281
282         if (!listing) return -2;
283         if (*listing) return -2;        /* Free the listing first */
284         if (!cret) return -2;
285         /* if (which < 0 || which > 4) return -2; */
286         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
287
288         sprintf(aaa, "%s %d", proto[which], floor);
289         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
290         if (ret / 100 == 1) {
291                 struct march *mptr;
292
293                 while (bbb && strlen(bbb)) {
294                         int a;
295
296                         extract_token(aaa, bbb, 0, '\n');
297                         a = strlen(aaa);
298                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
299                         mptr = (struct march *) malloc(sizeof (struct march));
300                         if (mptr) {
301                                 mptr->next = NULL;
302                                 extract(mptr->march_name, aaa, 0);
303                                 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
304                                 mptr->march_floor = (char) extract_int(aaa, 2);
305                                 mptr->march_order = (char) extract_int(aaa, 3);
306                                 if (march == NULL)
307                                         march = mptr;
308                                 else {
309                                         struct march *mptr2;
310
311                                         mptr2 = march;
312                                         while (mptr2->next != NULL)
313                                                 mptr2 = mptr2->next;
314                                         mptr2->next = mptr;
315                                 }
316                         }
317                 }
318         }
319         *listing = march;
320         return ret;
321 }
322
323
324 /* GETU */
325 /* Caller must free the struct usersupp; caller may pass an existing one */
326 int CtdlIPCGetConfig(CtdlIPC *ipc, struct usersupp **uret, char *cret)
327 {
328         register int ret;
329
330         if (!cret) return -2;
331         if (!uret) return -2;
332         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
333         if (!*uret) return -1;
334
335         ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
336         if (ret / 100 == 2) {
337                 uret[0]->USscreenwidth = extract_int(cret, 0);
338                 uret[0]->USscreenheight = extract_int(cret, 1);
339                 uret[0]->flags = extract_int(cret, 2);
340         }
341         return ret;
342 }
343
344
345 /* SETU */
346 int CtdlIPCSetConfig(CtdlIPC *ipc, struct usersupp *uret, char *cret)
347 {
348         char aaa[48];
349
350         if (!uret) return -2;
351         if (!cret) return -2;
352
353         sprintf(aaa, "SETU %d|%d|%d",
354                         uret->USscreenwidth, uret->USscreenheight,
355                         uret->flags);
356         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
357 }
358
359
360 /* GOTO */
361 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
362                 struct ctdlipcroom **rret, char *cret)
363 {
364         register int ret;
365         char *aaa;
366
367         if (!cret) return -2;
368         if (!rret) return -2;
369         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
370         if (!*rret) return -1;
371
372         if (passwd) {
373                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
374                 if (!aaa) {
375                         free(*rret);
376                         return -1;
377                 }
378                 sprintf(aaa, "GOTO %s|%s", room, passwd);
379         } else {
380                 aaa = (char *)malloc(strlen(room) + 6);
381                 if (!aaa) {
382                         free(*rret);
383                         return -1;
384                 }
385                 sprintf(aaa, "GOTO %s", room);
386         }
387         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
388         if (ret / 100 == 2) {
389                 extract(rret[0]->RRname, cret, 0);
390                 rret[0]->RRunread = extract_long(cret, 1);
391                 rret[0]->RRtotal = extract_long(cret, 2);
392                 rret[0]->RRinfoupdated = extract_int(cret, 3);
393                 rret[0]->RRflags = extract_int(cret, 4);
394                 rret[0]->RRhighest = extract_long(cret, 5);
395                 rret[0]->RRlastread = extract_long(cret, 6);
396                 rret[0]->RRismailbox = extract_int(cret, 7);
397                 rret[0]->RRaide = extract_int(cret, 8);
398                 rret[0]->RRnewmail = extract_long(cret, 9);
399                 rret[0]->RRfloor = extract_int(cret, 10);
400         } else {
401                 free(*rret);
402         }
403         return ret;
404 }
405
406
407 /* MSGS */
408 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
409 /* whicharg is number of messages, applies to last, first, gt, lt */
410 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
411                 const char *mtemplate, unsigned long **mret, char *cret)
412 {
413         register int ret;
414         register unsigned long count = 0;
415         static char *proto[] =
416                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
417         char aaa[33];
418         char *bbb = NULL;
419         size_t bbbsize;
420
421         if (!cret) return -2;
422         if (!mret) return -2;
423         if (*mret) return -2;
424         if (which < 0 || which > 6) return -2;
425
426         if (which <= 2)
427                 sprintf(aaa, "MSGS %s||%d", proto[which],
428                                 (mtemplate) ? 1 : 0);
429         else
430                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
431                                 (mtemplate) ? 1 : 0);
432         if (mtemplate) count = strlen(mtemplate);
433         ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbbsize, cret);
434         if (ret / 100 != 1)
435                 return ret;
436         count = 0;
437         *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
438         if (!*mret)
439                 return -1;
440         while (bbb && strlen(bbb)) {
441                 extract_token(aaa, bbb, 0, '\n');
442                 remove_token(bbb, 0, '\n');
443                 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
444                                         sizeof (unsigned long)));
445                 if (*mret) {
446                         (*mret)[count++] = atol(aaa);
447                         (*mret)[count] = 0L;
448                 } else {
449                         break;
450                 }
451         }
452         if (bbb) free(bbb);
453         return ret;
454 }
455
456
457 /* MSG0, MSG2 */
458 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
459                 struct ctdlipcmessage **mret, char *cret)
460 {
461         register int ret;
462         char aaa[SIZ];
463         char *bbb = NULL;
464         size_t bbbsize;
465         int multipart_hunting = 0;
466         char multipart_prefix[SIZ];
467
468         if (!cret) return -1;
469         if (!mret) return -1;
470         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
471         if (!*mret) return -1;
472         if (!msgnum) return -1;
473
474         strcpy(mret[0]->content_type, "");
475         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
476         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
477         if (ret / 100 == 1) {
478                 if (as_mime != 2) {
479                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
480                         while (strlen(bbb) > 4 && bbb[4] == '=') {
481                                 extract_token(aaa, bbb, 0, '\n');
482                                 remove_token(bbb, 0, '\n');
483
484                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
485                                         mret[0]->nhdr = 1;
486                                 else if (!strncasecmp(aaa, "from=", 5))
487                                         strcpy(mret[0]->author, &aaa[5]);
488                                 else if (!strncasecmp(aaa, "type=", 5))
489                                         mret[0]->type = atoi(&aaa[5]);
490                                 else if (!strncasecmp(aaa, "msgn=", 5))
491                                         strcpy(mret[0]->msgid, &aaa[5]);
492                                 else if (!strncasecmp(aaa, "subj=", 5))
493                                         strcpy(mret[0]->subject, &aaa[5]);
494                                 else if (!strncasecmp(aaa, "rfca=", 5))
495                                         strcpy(mret[0]->email, &aaa[5]);
496                                 else if (!strncasecmp(aaa, "hnod=", 5))
497                                         strcpy(mret[0]->hnod, &aaa[5]);
498                                 else if (!strncasecmp(aaa, "room=", 5))
499                                         strcpy(mret[0]->room, &aaa[5]);
500                                 else if (!strncasecmp(aaa, "node=", 5))
501                                         strcpy(mret[0]->node, &aaa[5]);
502                                 else if (!strncasecmp(aaa, "rcpt=", 5))
503                                         strcpy(mret[0]->recipient, &aaa[5]);
504                                 else if (!strncasecmp(aaa, "time=", 5))
505                                         mret[0]->time = atol(&aaa[5]);
506
507                                 /* Multipart/alternative prefix & suffix strings help
508                                  * us to determine which part we want to download.
509                                  */
510                                 else if (!strncasecmp(aaa, "pref=", 5)) {
511                                         extract(multipart_prefix, &aaa[5], 1);
512                                         if (!strcasecmp(multipart_prefix,
513                                            "multipart/alternative")) {
514                                                 ++multipart_hunting;
515                                         }
516                                 }
517                                 else if (!strncasecmp(aaa, "suff=", 5)) {
518                                         extract(multipart_prefix, &aaa[5], 1);
519                                         if (!strcasecmp(multipart_prefix,
520                                            "multipart/alternative")) {
521                                                 ++multipart_hunting;
522                                         }
523                                 }
524
525                                 else if (!strncasecmp(aaa, "part=", 5)) {
526                                         struct parts *ptr, *chain;
527         
528                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
529                                         if (ptr) {
530
531                                                 /* Fill the buffers for the caller */
532                                                 extract(ptr->name, &aaa[5], 0);
533                                                 extract(ptr->filename, &aaa[5], 1);
534                                                 extract(ptr->number, &aaa[5], 2);
535                                                 extract(ptr->disposition, &aaa[5], 3);
536                                                 extract(ptr->mimetype, &aaa[5], 4);
537                                                 ptr->length = extract_long(&aaa[5], 5);
538                                                 if (!mret[0]->attachments)
539                                                         mret[0]->attachments = ptr;
540                                                 else {
541                                                         chain = mret[0]->attachments;
542                                                         while (chain->next)
543                                                                 chain = chain->next;
544                                                         chain->next = ptr;
545                                                 }
546
547                                                 /* Now handle multipart/alternative */
548                                                 if (multipart_hunting > 0) {
549                                                         if ( (!strcasecmp(ptr->mimetype,
550                                                              "text/plain"))
551                                                            || (!strcasecmp(ptr->mimetype,
552                                                               "text/html")) ) {
553                                                                 strcpy(mret[0]->mime_chosen,
554                                                                         ptr->number);
555                                                         }
556                                                 }
557
558                                         }
559                                 }
560                         }
561                         /* Eliminate "text\n" */
562                         remove_token(bbb, 0, '\n');
563
564                         /* If doing a MIME thing, pull out the extra headers */
565                         if (as_mime == 4) {
566                                 do {
567                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
568                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
569                                                 strcpy(mret[0]->content_type,
570                                                         &mret[0]->content_type[14]);
571                                                 striplt(mret[0]->content_type);
572                                         }
573                                         remove_token(bbb, 0, '\n');
574                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
575                 /*ooga*/                remove_token(bbb, 0, '\n');
576                         }
577
578
579                 }
580                 if (strlen(bbb)) {
581                         /* FIXME: Strip trailing whitespace */
582                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
583                 } else {
584                         bbb = (char *)realloc(bbb, 1);
585                         *bbb = '\0';
586                 }
587                 mret[0]->text = bbb;
588         }
589         return ret;
590 }
591
592
593 /* WHOK */
594 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
595 {
596         register int ret;
597         size_t bytes;
598
599         if (!cret) return -2;
600         if (!listing) return -2;
601         if (*listing) return -2;
602
603         ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
604         return ret;
605 }
606
607
608 /* INFO */
609 int CtdlIPCServerInfo(CtdlIPC *ipc, struct CtdlServInfo *ServInfo, char *cret)
610 {
611         register int ret;
612         size_t bytes;
613         char *listing = NULL;
614         char buf[SIZ];
615
616         if (!cret) return -2;
617         if (!ServInfo) return -2;
618
619         ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
620         if (ret / 100 == 1) {
621                 int line = 0;
622
623                 while (*listing && strlen(listing)) {
624                         extract_token(buf, listing, 0, '\n');
625                         remove_token(listing, 0, '\n');
626                         switch (line++) {
627                         case 0:         ServInfo->serv_pid = atoi(buf);
628                                         break;
629                         case 1:         strcpy(ServInfo->serv_nodename,buf);
630                                         break;
631                         case 2:         strcpy(ServInfo->serv_humannode,buf);
632                                         break;
633                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
634                                         break;
635                         case 4:         strcpy(ServInfo->serv_software,buf);
636                                         break;
637                         case 5:         ServInfo->serv_rev_level = atoi(buf);
638                                         break;
639                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
640                                         break;
641                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
642                                         break;
643                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
644                                         break;
645                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
646                                         break;
647                         case 11:        ServInfo->serv_paging_level = atoi(buf);
648                                         break;
649                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
650                                         break;
651                         }
652                 }
653
654         }
655         return ret;
656 }
657
658
659 /* RDIR */
660 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
661 {
662         register int ret;
663         size_t bytes;
664
665         if (!cret) return -2;
666         if (!listing) return -2;
667         if (*listing) return -2;
668
669         ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
670         return ret;
671 }
672
673
674 /*
675  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
676  */
677 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
678 {
679         register int ret;
680         char aaa[16];
681
682         if (!cret) return -2;
683
684         if (msgnum)
685                 sprintf(aaa, "SLRP %ld", msgnum);
686         else
687                 sprintf(aaa, "SLRP HIGHEST");
688         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
689         return ret;
690 }
691
692
693 /* INVT */
694 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
695 {
696         register int ret;
697         char *aaa;
698
699         if (!cret) return -2;
700         if (!username) return -2;
701
702         aaa = (char *)malloc(strlen(username) + 6);
703         if (!aaa) return -1;
704
705         sprintf(aaa, "INVT %s", username);
706         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
707         free(aaa);
708         return ret;
709 }
710
711
712 /* KICK */
713 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
714 {
715         register int ret;
716         char *aaa;
717
718         if (!cret) return -1;
719         if (!username) return -1;
720
721         aaa = (char *)malloc(strlen(username) + 6);
722
723         sprintf(aaa, "KICK %s", username);
724         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
725         free(aaa);
726         return ret;
727 }
728
729
730 /* GETR */
731 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
732 {
733         register int ret;
734
735         if (!cret) return -2;
736         if (!qret) return -2;
737         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
738         if (!*qret) return -1;
739
740         ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
741         if (ret / 100 == 2) {
742                 extract(qret[0]->QRname, cret, 0);
743                 extract(qret[0]->QRpasswd, cret, 1);
744                 extract(qret[0]->QRdirname, cret, 2);
745                 qret[0]->QRflags = extract_int(cret, 3);
746                 qret[0]->QRfloor = extract_int(cret, 4);
747                 qret[0]->QRorder = extract_int(cret, 5);
748                 qret[0]->QRdefaultview = extract_int(cret, 6);
749                 qret[0]->QRflags2 = extract_int(cret, 7);
750         }
751         return ret;
752 }
753
754
755 /* SETR */
756 /* set forget to kick all users out of room */
757 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct quickroom *qret, char *cret)
758 {
759         register int ret;
760         char *aaa;
761
762         if (!cret) return -2;
763         if (!qret) return -2;
764
765         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
766                         strlen(qret->QRdirname) + 52);
767         if (!aaa) return -1;
768
769         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
770                         qret->QRname, qret->QRpasswd, qret->QRdirname,
771                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
772         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
773         free(aaa);
774         return ret;
775 }
776
777
778 /* GETA */
779 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
780 {
781         if (!cret) return -1;
782
783         return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
784 }
785
786
787 /* SETA */
788 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
789 {
790         register int ret;
791         char *aaa;
792
793         if (!cret) return -2;
794         if (!username) return -2;
795
796         aaa = (char *)malloc(strlen(username) + 6);
797         if (!aaa) return -1;
798
799         sprintf(aaa, "SETA %s", username);
800         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
801         free(aaa);
802         return ret;
803 }
804
805
806 /* ENT0 */
807 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
808 {
809         register int ret;
810         char *aaa;
811
812         if (!cret) return -2;
813         if (!mr) return -2;
814
815         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
816         if (!aaa) return -1;
817
818         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
819                         mr->anonymous, mr->type, mr->subject, mr->author);
820         ret = CtdlIPCGenericCommand(ipc, aaa, mr->text, strlen(mr->text), NULL,
821                         NULL, cret);
822         free(aaa);
823         return ret;
824 }
825
826
827 /* RINF */
828 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
829 {
830         size_t bytes;
831
832         if (!cret) return -2;
833         if (!iret) return -2;
834         if (*iret) return -2;
835
836         return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
837 }
838
839
840 /* DELE */
841 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
842 {
843         char aaa[16];
844
845         if (!cret) return -2;
846         if (!msgnum) return -2;
847
848         sprintf(aaa, "DELE %ld", msgnum);
849         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
850 }
851
852
853 /* MOVE */
854 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
855 {
856         register int ret;
857         char *aaa;
858
859         if (!cret) return -2;
860         if (!destroom) return -2;
861         if (!msgnum) return -2;
862
863         aaa = (char *)malloc(strlen(destroom) + 28);
864         if (!aaa) return -1;
865
866         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
867         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
868         free(aaa);
869         return ret;
870 }
871
872
873 /* KILL */
874 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
875 {
876         char aaa[16];
877
878         if (!cret) return -2;
879
880         sprintf(aaa, "KILL %d", for_real);
881         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
882 }
883
884
885 /* CRE8 */
886 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
887                 const char *password, int floor, char *cret)
888 {
889         register int ret;
890         char *aaa;
891
892         if (!cret) return -2;
893         if (!roomname) return -2;
894
895         if (password) {
896                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
897                 if (!aaa) return -1;
898                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
899                                 password, floor);
900         } else {
901                 aaa = (char *)malloc(strlen(roomname) + 40);
902                 if (!aaa) return -1;
903                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
904                                 floor);
905         }
906         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
907         free(aaa);
908         return ret;
909 }
910
911
912 /* FORG */
913 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
914 {
915         if (!cret) return -2;
916
917         return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
918 }
919
920
921 /* MESG */
922 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
923 {
924         register int ret;
925         char *aaa;
926         size_t bytes;
927
928         if (!cret) return -2;
929         if (!mret) return -2;
930         if (*mret) return -2;
931         if (!message) return -2;
932
933         aaa = (char *)malloc(strlen(message) + 6);
934         if (!aaa) return -1;
935
936         sprintf(aaa, "MESG %s", message);
937         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
938         free(aaa);
939         return ret;
940 }
941
942
943 /* GNUR */
944 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
945 {
946         if (!cret) return -2;
947
948         return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
949 }
950
951
952 /* GREG */
953 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
954 {
955         register int ret;
956         char *aaa;
957         size_t bytes;
958
959         if (!cret) return -2;
960         if (!rret) return -2;
961         if (*rret) return -2;
962
963         if (username)
964                 aaa = (char *)malloc(strlen(username) + 6);
965         else
966                 aaa = (char *)malloc(12);
967         if (!aaa) return -1;
968
969         if (username)
970                 sprintf(aaa, "GREG %s", username);
971         else
972                 sprintf(aaa, "GREG _SELF_");
973         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
974         free(aaa);
975         return ret;
976 }
977
978
979 /* VALI */
980 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
981 {
982         register int ret;
983         char *aaa;
984
985         if (!cret) return -2;
986         if (!username) return -2;
987         if (axlevel < 0 || axlevel > 7) return -2;
988
989         aaa = (char *)malloc(strlen(username) + 17);
990         if (!aaa) return -1;
991
992         sprintf(aaa, "VALI %s|%d", username, axlevel);
993         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
994         free(aaa);
995         return ret;
996 }
997
998
999 /* EINF */
1000 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1001 {
1002         char aaa[16];
1003
1004         if (!cret) return -1;
1005         if (!info) return -1;
1006
1007         sprintf(aaa, "EINF %d", for_real);
1008         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1009 }
1010
1011
1012 /* LIST */
1013 int CtdlIPCUserListing(CtdlIPC *ipc, char **listing, char *cret)
1014 {
1015         size_t bytes;
1016
1017         if (!cret) return -1;
1018         if (!listing) return -1;
1019         if (*listing) return -1;
1020
1021         return CtdlIPCGenericCommand(ipc, "LIST", NULL, 0, listing, &bytes, cret);
1022 }
1023
1024
1025 /* REGI */
1026 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1027 {
1028         if (!cret) return -1;
1029         if (!info) return -1;
1030
1031         return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1032                         NULL, NULL, cret);
1033 }
1034
1035
1036 /* CHEK */
1037 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1038 {
1039         register int ret;
1040
1041         if (!cret) return -1;
1042         if (!chek) return -1;
1043
1044         ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1045         if (ret / 100 == 2) {
1046                 chek->newmail = extract_long(cret, 0);
1047                 chek->needregis = extract_int(cret, 1);
1048                 chek->needvalid = extract_int(cret, 2);
1049         }
1050         return ret;
1051 }
1052
1053
1054 /* DELF */
1055 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1056 {
1057         register int ret;
1058         char *aaa;
1059
1060         if (!cret) return -2;
1061         if (!filename) return -2;
1062         
1063         aaa = (char *)malloc(strlen(filename) + 6);
1064         if (!aaa) return -1;
1065
1066         sprintf(aaa, "DELF %s", filename);
1067         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1068         free(aaa);
1069         return ret;
1070 }
1071
1072
1073 /* MOVF */
1074 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1075 {
1076         register int ret;
1077         char *aaa;
1078
1079         if (!cret) return -2;
1080         if (!filename) return -2;
1081         if (!destroom) return -2;
1082
1083         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1084         if (!aaa) return -1;
1085
1086         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1087         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1088         free(aaa);
1089         return ret;
1090 }
1091
1092
1093 /* NETF */
1094 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1095 {
1096         register int ret;
1097         char *aaa;
1098
1099         if (!cret) return -2;
1100         if (!filename) return -2;
1101         if (!destnode) return -2;
1102
1103         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1104         if (!aaa) return -1;
1105
1106         sprintf(aaa, "NETF %s|%s", filename, destnode);
1107         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1108         free(aaa);
1109         return ret;
1110 }
1111
1112
1113 /* RWHO */
1114 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1115 {
1116         register int ret;
1117         size_t bytes;
1118
1119         if (!cret) return -1;
1120         if (!listing) return -1;
1121         if (*listing) return -1;
1122
1123         *stamp = CtdlIPCServerTime(ipc, cret);
1124         if (!*stamp)
1125                 *stamp = time(NULL);
1126         ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1127         return ret;
1128 }
1129
1130
1131 /* OPEN */
1132 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1133                 size_t resume,
1134                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1135                 char *cret)
1136 {
1137         register int ret;
1138         size_t bytes;
1139         time_t last_mod;
1140         char mimetype[SIZ];
1141         char *aaa;
1142
1143         if (!cret) return -2;
1144         if (!filename) return -2;
1145         if (!buf) return -2;
1146         if (*buf) return -2;
1147         if (ipc->downloading) return -2;
1148
1149         aaa = (char *)malloc(strlen(filename) + 6);
1150         if (!aaa) return -1;
1151
1152         sprintf(aaa, "OPEN %s", filename);
1153         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1154         free(aaa);
1155         if (ret / 100 == 2) {
1156                 ipc->downloading = 1;
1157                 bytes = extract_long(cret, 0);
1158                 last_mod = extract_int(cret, 1);
1159                 extract(mimetype, cret, 2);
1160 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret); */
1161                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1162                 ret = CtdlIPCEndDownload(ipc, cret);
1163                 if (ret / 100 == 2)
1164                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1165                                         filename, mimetype);
1166         }
1167         return ret;
1168 }
1169
1170
1171 /* OPNA */
1172 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1173                 void **buf,
1174                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1175                 char *cret)
1176 {
1177         register int ret;
1178         size_t bytes;
1179         time_t last_mod;
1180         char filename[SIZ];
1181         char mimetype[SIZ];
1182         char *aaa;
1183
1184         if (!cret) return -2;
1185         if (!buf) return -2;
1186         if (*buf) return -2;
1187         if (!part) return -2;
1188         if (!msgnum) return -2;
1189         if (ipc->downloading) return -2;
1190
1191         aaa = (char *)malloc(strlen(part) + 17);
1192         if (!aaa) return -1;
1193
1194         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1195         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1196         free(aaa);
1197         if (ret / 100 == 2) {
1198                 ipc->downloading = 1;
1199                 bytes = extract_long(cret, 0);
1200                 last_mod = extract_int(cret, 1);
1201                 extract(mimetype, cret, 2);
1202 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1203                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1204                 ret = CtdlIPCEndDownload(ipc, cret);
1205                 if (ret / 100 == 2)
1206                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1207                                         filename, mimetype);
1208         }
1209         return ret;
1210 }
1211
1212
1213 /* OIMG */
1214 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1215                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1216                 char *cret)
1217 {
1218         register int ret;
1219         size_t bytes;
1220         time_t last_mod;
1221         char mimetype[SIZ];
1222         char *aaa;
1223
1224         if (!cret) return -1;
1225         if (!buf) return -1;
1226         if (*buf) return -1;
1227         if (!filename) return -1;
1228         if (ipc->downloading) return -1;
1229
1230         aaa = (char *)malloc(strlen(filename) + 6);
1231         if (!aaa) return -1;
1232
1233         sprintf(aaa, "OIMG %s", filename);
1234         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1235         free(aaa);
1236         if (ret / 100 == 2) {
1237                 ipc->downloading = 1;
1238                 bytes = extract_long(cret, 0);
1239                 last_mod = extract_int(cret, 1);
1240                 extract(mimetype, cret, 2);
1241 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1242                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1243                 ret = CtdlIPCEndDownload(ipc, cret);
1244                 if (ret / 100 == 2)
1245                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1246                                         filename, mimetype);
1247         }
1248         return ret;
1249 }
1250
1251
1252 /* UOPN */
1253 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1254                 const char *path,
1255                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1256                 char *cret)
1257 {
1258         register int ret;
1259         char *aaa;
1260
1261         if (!cret) return -1;
1262         if (!save_as) return -1;
1263         if (!comment) return -1;
1264         if (!path) return -1;
1265         if (!*path) return -1;
1266         if (ipc->uploading) return -1;
1267
1268         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1269         if (!aaa) return -1;
1270
1271         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1272         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1273         free(aaa);
1274         if (ret / 100 == 2) {
1275                 ipc->uploading = 1;
1276                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1277                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1278                 ipc->uploading = 0;
1279         }
1280         return ret;
1281 }
1282
1283
1284 /* UIMG */
1285 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1286                 const char *save_as,
1287                 void (*progress_gauge_callback)(unsigned long, unsigned long),
1288                 char *cret)
1289 {
1290         register int ret;
1291         char *aaa;
1292
1293         if (!cret) return -1;
1294         if (!save_as) return -1;
1295         if (!path && for_real) return -1;
1296         if (!*path && for_real) return -1;
1297         if (ipc->uploading) return -1;
1298
1299         aaa = (char *)malloc(strlen(save_as) + 17);
1300         if (!aaa) return -1;
1301
1302         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1303         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1304         free(aaa);
1305         if (ret / 100 == 2 && for_real) {
1306                 ipc->uploading = 1;
1307                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1308                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1309                 ipc->uploading = 0;
1310         }
1311         return ret;
1312 }
1313
1314
1315 /* QUSR */
1316 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1317 {
1318         register int ret;
1319         char *aaa;
1320
1321         if (!cret) return -2;
1322         if (!username) return -2;
1323
1324         aaa = (char *)malloc(strlen(username) + 6);
1325         if (!aaa) return -1;
1326
1327         sprintf(aaa, "QUSR %s", username);
1328         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1329         free(aaa);
1330         return ret;
1331 }
1332
1333
1334 /* LFLR */
1335 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1336 {
1337         size_t bytes;
1338
1339         if (!cret) return -2;
1340         if (!listing) return -2;
1341         if (*listing) return -2;
1342
1343         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1344 }
1345
1346
1347 /* CFLR */
1348 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1349 {
1350         register int ret;
1351         char *aaa;
1352
1353         if (!cret) return -2;
1354         if (!name) return -2;
1355         if (!*name) return -2;
1356
1357         aaa = (char *)malloc(strlen(name) + 17);
1358         if (!aaa) return -1;
1359
1360         sprintf(aaa, "CFLR %s|%d", name, for_real);
1361         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1362         free(aaa);
1363         return ret;
1364 }
1365
1366
1367 /* KFLR */
1368 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1369 {
1370         char aaa[27];
1371
1372         if (!cret) return -1;
1373         if (floornum < 0) return -1;
1374
1375         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1376         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1377 }
1378
1379
1380 /* EFLR */
1381 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1382 {
1383         register int ret;
1384         char *aaa;
1385
1386         if (!cret) return -2;
1387         if (!floorname) return -2;
1388         if (floornum < 0) return -2;
1389
1390         aaa = (char *)malloc(strlen(floorname) + 17);
1391         if (!aaa) return -1;
1392
1393         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1394         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1395         free(aaa);
1396         return ret;
1397 }
1398
1399
1400 /*
1401  * IDEN 
1402  *
1403  * You only need to fill out hostname, the defaults will be used if any of the
1404  * other fields are not set properly.
1405  */
1406 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1407                 int revision, const char *software_name, const char *hostname,
1408                 char *cret)
1409 {
1410         register int ret;
1411         char *aaa;
1412
1413         if (developerid < 0 || clientid < 0 || revision < 0 ||
1414             !software_name) {
1415                 developerid = 8;
1416                 clientid = 0;
1417                 revision = REV_LEVEL - 600;
1418                 software_name = "Citadel/UX (libcitadel)";
1419         }
1420         if (!hostname) return -2;
1421
1422         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1423         if (!aaa) return -1;
1424
1425         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1426                         revision, software_name, hostname);
1427         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1428         free(aaa);
1429         return ret;
1430 }
1431
1432
1433 /* SEXP */
1434 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1435                 char *cret)
1436 {
1437         register int ret;
1438         char *aaa;
1439
1440         if (!cret) return -2;
1441         if (!username) return -2;
1442
1443         aaa = (char *)malloc(strlen(username) + 8);
1444         if (!aaa) return -1;
1445
1446         if (text) {
1447                 sprintf(aaa, "SEXP %s|-", username);
1448                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1449                                 NULL, NULL, cret);
1450         } else {
1451                 sprintf(aaa, "SEXP %s||", username);
1452                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1453         }
1454         free(aaa);
1455         return ret;
1456 }
1457
1458
1459 /* GEXP */
1460 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1461 {
1462         size_t bytes;
1463
1464         if (!cret) return -2;
1465         if (!listing) return -2;
1466         if (*listing) return -2;
1467
1468         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1469 }
1470
1471
1472 /* DEXP */
1473 /* mode is 0 = enable, 1 = disable, 2 = status */
1474 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1475 {
1476         char aaa[16];
1477
1478         if (!cret) return -2;
1479
1480         sprintf(aaa, "DEXP %d", mode);
1481         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1482 }
1483
1484
1485 /* EBIO */
1486 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1487 {
1488         if (!cret) return -2;
1489         if (!bio) return -2;
1490
1491         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1492                         NULL, NULL, cret);
1493 }
1494
1495
1496 /* RBIO */
1497 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1498 {
1499         register int ret;
1500         size_t bytes;
1501         char *aaa;
1502
1503         if (!cret) return -2;
1504         if (!username) return -2;
1505         if (!listing) return -2;
1506         if (*listing) return -2;
1507
1508         aaa = (char *)malloc(strlen(username) + 6);
1509         if (!aaa) return -1;
1510
1511         sprintf(aaa, "RBIO %s", username);
1512         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1513         free(aaa);
1514         return ret;
1515 }
1516
1517
1518 /* LBIO */
1519 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1520 {
1521         size_t bytes;
1522
1523         if (!cret) return -2;
1524         if (!listing) return -2;
1525         if (*listing) return -2;
1526
1527         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1528 }
1529
1530
1531 /* STEL */
1532 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1533 {
1534         char aaa[16];
1535
1536         if (!cret) return -1;
1537
1538         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1539         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1540 }
1541
1542
1543 /* TERM */
1544 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1545 {
1546         char aaa[16];
1547
1548         if (!cret) return -1;
1549
1550         sprintf(aaa, "TERM %d", sid);
1551         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1552 }
1553
1554
1555 /* DOWN */
1556 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1557 {
1558         if (!cret) return -1;
1559
1560         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1561 }
1562
1563
1564 /* SCDN */
1565 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1566 {
1567         char aaa[16];
1568
1569         if (!cret) return -1;
1570
1571         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1572         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1573 }
1574
1575
1576 /* EMSG */
1577 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1578                 char *cret)
1579 {
1580         register int ret;
1581         char *aaa;
1582
1583         if (!cret) return -2;
1584         if (!text) return -2;
1585         if (!filename) return -2;
1586
1587         aaa = (char *)malloc(strlen(filename) + 6);
1588         if (!aaa) return -1;
1589
1590         sprintf(aaa, "EMSG %s", filename);
1591         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1592         free(aaa);
1593         return ret;
1594 }
1595
1596
1597 /* HCHG */
1598 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1599 {
1600         register int ret;
1601         char *aaa;
1602
1603         if (!cret) return -2;
1604         if (!hostname) return -2;
1605
1606         aaa = (char *)malloc(strlen(hostname) + 6);
1607         if (!aaa) return -1;
1608
1609         sprintf(aaa, "HCHG %s", hostname);
1610         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1611         free(aaa);
1612         return ret;
1613 }
1614
1615
1616 /* RCHG */
1617 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1618 {
1619         register int ret;
1620         char *aaa;
1621
1622         if (!cret) return -2;
1623         if (!roomname) return -2;
1624
1625         aaa = (char *)malloc(strlen(roomname) + 6);
1626         if (!aaa) return -1;
1627
1628         sprintf(aaa, "RCHG %s", roomname);
1629         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1630         free(aaa);
1631         return ret;
1632 }
1633
1634
1635 /* UCHG */
1636 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1637 {
1638         register int ret;
1639         char *aaa;
1640
1641         if (!cret) return -2;
1642         if (!username) return -2;
1643
1644         aaa = (char *)malloc(strlen(username) + 6);
1645         if (!aaa) return -1;
1646
1647         sprintf(aaa, "UCHG %s", username);
1648         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1649         free(aaa);
1650         return ret;
1651 }
1652
1653
1654 /* TIME */
1655 /* This function returns the actual server time reported, or 0 if error */
1656 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1657 {
1658         register time_t tret;
1659         register int ret;
1660
1661         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1662         if (ret / 100 == 2) {
1663                 tret = extract_long(cret, 0);
1664         } else {
1665                 tret = 0L;
1666         }
1667         return tret;
1668 }
1669
1670
1671 /* AGUP */
1672 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1673                                  struct usersupp **uret, char *cret)
1674 {
1675         register int ret;
1676         char aaa[SIZ];
1677
1678         if (!cret) return -2;
1679         if (!uret) return -2;
1680         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1681         if (!*uret) return -1;
1682
1683         sprintf(aaa, "AGUP %s", who);
1684         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1685
1686         if (ret / 100 == 2) {
1687                 extract(uret[0]->fullname, cret, 0);
1688                 extract(uret[0]->password, cret, 1);
1689                 uret[0]->flags = extract_int(cret, 2);
1690                 uret[0]->timescalled = extract_long(cret, 3);
1691                 uret[0]->posted = extract_long(cret, 4);
1692                 uret[0]->axlevel = extract_int(cret, 5);
1693                 uret[0]->usernum = extract_long(cret, 6);
1694                 uret[0]->lastcall = extract_long(cret, 7);
1695                 uret[0]->USuserpurge = extract_int(cret, 8);
1696         }
1697         return ret;
1698 }
1699
1700
1701 /* ASUP */
1702 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char *cret)
1703 {
1704         register int ret;
1705         char *aaa;
1706
1707         if (!cret) return -2;
1708         if (!uret) return -2;
1709
1710         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1711         if (!aaa) return -1;
1712
1713         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1714                         uret->fullname, uret->password, uret->flags,
1715                         uret->timescalled, uret->posted, uret->axlevel,
1716                         uret->usernum, uret->lastcall, uret->USuserpurge);
1717         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1718         free(aaa);
1719         return ret;
1720 }
1721
1722
1723 /* GPEX */
1724 /* which is 0 = room, 1 = floor, 2 = site */
1725 /* caller must free the struct ExpirePolicy */
1726 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1727                 struct ExpirePolicy **policy, char *cret)
1728 {
1729         static char *proto[] = {"room", "floor", "site"};
1730         char aaa[11];
1731         register int ret;
1732
1733         if (!cret) return -2;
1734         if (!policy) return -2;
1735         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1736         if (!*policy) return -1;
1737         if (which < 0 || which > 2) return -2;
1738         
1739         sprintf(aaa, "GPEX %s", proto[which]);
1740         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1741         if (ret / 100 == 2) {
1742                 policy[0]->expire_mode = extract_int(cret, 0);
1743                 policy[0]->expire_value = extract_int(cret, 1);
1744         }
1745         return ret;
1746
1747 }
1748
1749
1750 /* SPEX */
1751 /* which is 0 = room, 1 = floor, 2 = site */
1752 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1753 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1754                 struct ExpirePolicy *policy, char *cret)
1755 {
1756         char aaa[38];
1757         char *whichvals[] = { "room", "floor", "site" };
1758
1759         if (!cret) return -2;
1760         if (which < 0 || which > 2) return -2;
1761         if (!policy) return -2;
1762         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1763         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1764
1765         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1766                         policy->expire_mode, policy->expire_value);
1767         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1768 }
1769
1770
1771 /* CONF GET */
1772 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1773 {
1774         size_t bytes;
1775
1776         if (!cret) return -2;
1777         if (!listing) return -2;
1778         if (*listing) return -2;
1779
1780         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1781                         listing, &bytes, cret);
1782 }
1783
1784
1785 /* CONF SET */
1786 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1787 {
1788         if (!cret) return -2;
1789         if (!listing) return -2;
1790
1791         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1792                         NULL, NULL, cret);
1793 }
1794
1795
1796 /* CONF GETSYS */
1797 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1798                 char **listing, char *cret)
1799 {
1800         char *aaa;
1801         size_t bytes;
1802
1803         if (!cret) return -2;
1804         if (!mimetype) return -2;
1805         if (!listing) return -2;
1806         if (*listing) return -2;
1807
1808         aaa = malloc(strlen(mimetype) + 13);
1809         if (!aaa) return -1;
1810         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1811         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1812                         listing, &bytes, cret);
1813 }
1814
1815
1816 /* CONF PUTSYS */
1817 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1818                const char *listing, char *cret)
1819 {
1820         char *aaa;
1821
1822         if (!cret) return -2;
1823         if (!mimetype) return -2;
1824         if (!listing) return -2;
1825
1826         aaa = malloc(strlen(mimetype) + 13);
1827         if (!aaa) return -1;
1828         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1829         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1830                         NULL, NULL, cret);
1831 }
1832
1833
1834 /* REQT */
1835 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1836 {
1837         char aaa[16];
1838
1839         if (!cret) return -2;
1840         if (session < 0) return -2;
1841
1842         sprintf(aaa, "REQT %d", session);
1843         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1844 }
1845
1846
1847 /* SEEN */
1848 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1849 {
1850         char aaa[27];
1851
1852         if (!cret) return -2;
1853         if (msgnum < 0) return -2;
1854
1855         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1856         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1857 }
1858
1859
1860 /* STLS */
1861 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1862 {
1863         int a;
1864         int r;
1865         char buf[SIZ];
1866
1867 #ifdef HAVE_OPENSSL
1868         SSL *temp_ssl;
1869
1870         /* New SSL object */
1871         temp_ssl = SSL_new(ssl_ctx);
1872         if (!temp_ssl) {
1873                 error_printf("SSL_new failed: %s\n",
1874                                 ERR_reason_error_string(ERR_get_error()));
1875                 return -2;
1876         }
1877         /* Pointless flag waving */
1878 #if SSLEAY_VERSION_NUMBER >= 0x0922
1879         SSL_set_session_id_context(temp_ssl, "Citadel/UX SID", 14);
1880 #endif
1881
1882         if (!access("/var/run/egd-pool", F_OK))
1883                 RAND_egd("/var/run/egd-pool");
1884
1885         if (!RAND_status()) {
1886                 error_printf("PRNG not properly seeded\n");
1887                 return -2;
1888         }
1889
1890         /* Associate network connection with SSL object */
1891         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1892                 error_printf("SSL_set_fd failed: %s\n",
1893                                 ERR_reason_error_string(ERR_get_error()));
1894                 return -2;
1895         }
1896
1897         if (status_hook != NULL)
1898                 status_hook("Requesting encryption...\r");
1899
1900         /* Ready to start SSL/TLS */
1901         /* Old code
1902         CtdlIPC_putline(ipc, "STLS");
1903         CtdlIPC_getline(ipc, buf);
1904         if (buf[0] != '2') {
1905                 error_printf("Server can't start TLS: %s\n", buf);
1906                 return 0;
1907         }
1908         */
1909         r = CtdlIPCGenericCommand(ipc,
1910                                   "STLS", NULL, 0, NULL, NULL, cret);
1911         if (r / 100 != 2) {
1912                 error_printf("Server can't start TLS: %s\n", buf);
1913                 endtls(temp_ssl);
1914                 return r;
1915         }
1916
1917         /* Do SSL/TLS handshake */
1918         if ((a = SSL_connect(temp_ssl)) < 1) {
1919                 error_printf("SSL_connect failed: %s\n",
1920                                 ERR_reason_error_string(ERR_get_error()));
1921                 endtls(temp_ssl);
1922                 return -2;
1923         }
1924         ipc->ssl = temp_ssl;
1925
1926         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1927         {
1928                 int bits, alg_bits;
1929
1930                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1931                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1932                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1933                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1934                                 bits, alg_bits);
1935         }
1936         return r;
1937 #else
1938         return 0;
1939 #endif /* HAVE_OPENSSL */
1940 }
1941
1942
1943 #ifdef HAVE_OPENSSL
1944 static void endtls(SSL *ssl)
1945 {
1946         if (ssl) {
1947                 SSL_shutdown(ssl);
1948                 SSL_free(ssl);
1949         }
1950 }
1951 #endif
1952
1953
1954 /* QDIR */
1955 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
1956 {
1957         char *aaa;
1958
1959         if (!address) return -2;
1960         if (!cret) return -2;
1961
1962         aaa = (char *)malloc(strlen(address) + 6);
1963         if (!aaa) return -1;
1964
1965         sprintf(aaa, "QDIR %s", address);
1966         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1967 }
1968
1969
1970 /* IPGM */
1971 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
1972 {
1973         char aaa[30];
1974
1975         if (!cret) return -2;
1976         sprintf(aaa, "IPGM %d", secret);
1977         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1978 }
1979
1980
1981 /* FSCK */
1982 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
1983 {
1984         size_t size = 0;
1985
1986         if (!cret) return -2;
1987         if (!mret) return -2;
1988         if (*mret) return -2;
1989
1990         return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
1991 }
1992
1993
1994 /*
1995  * Not implemented:
1996  * 
1997  * CHAT
1998  * ETLS
1999  * EXPI
2000  * GTLS
2001  * IGAB
2002  * MSG3
2003  * MSG4
2004  * NDOP
2005  * NETP
2006  * NUOP
2007  * SMTP
2008  */
2009
2010
2011 /* ************************************************************************** */
2012 /*             Stuff below this line is not for public consumption            */
2013 /* ************************************************************************** */
2014
2015
2016 inline void CtdlIPC_lock(CtdlIPC *ipc)
2017 {
2018         if (ipc->network_status_cb) ipc->network_status_cb(1);
2019 #ifdef THREADED_CLIENT
2020         pthread_mutex_lock(&(ipc->mutex));
2021 #endif
2022 }
2023
2024
2025 inline void CtdlIPC_unlock(CtdlIPC *ipc)
2026 {
2027 #ifdef THREADED_CLIENT
2028         pthread_mutex_unlock(&(ipc->mutex));
2029 #endif
2030         if (ipc->network_status_cb) ipc->network_status_cb(0);
2031 }
2032
2033
2034 /* Read a listing from the server up to 000.  Append to dest if it exists */
2035 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2036 {
2037         size_t length = 0;
2038         size_t linelength;
2039         char *ret = NULL;
2040         char aaa[SIZ];
2041
2042         ret = dest;
2043         if (ret != NULL) {
2044                 length = strlen(ret);
2045         } else {
2046                 length = 0;
2047         }
2048
2049         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2050                 linelength = strlen(aaa);
2051                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2052                 if (ret) {
2053                         strcpy(&ret[length], aaa);
2054                         length += linelength;
2055                         strcpy(&ret[length++], "\n");
2056                 }
2057         }
2058
2059         return(ret);
2060 }
2061
2062
2063 /* Send a listing to the server; generate the ending 000. */
2064 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2065 {
2066         char *text;
2067
2068         text = (char *)malloc(strlen(listing) + 6);
2069         if (text) {
2070                 strcpy(text, listing);
2071                 while (text[strlen(text) - 1] == '\n')
2072                         text[strlen(text) - 1] = '\0';
2073                 strcat(text, "\n000");
2074                 CtdlIPC_putline(ipc, text);
2075                 free(text);
2076                 text = NULL;
2077         } else {
2078                 /* Malloc failed but we are committed to send */
2079                 /* This may result in extra blanks at the bottom */
2080                 CtdlIPC_putline(ipc, text);
2081                 CtdlIPC_putline(ipc, "000");
2082         }
2083         return 0;
2084 }
2085
2086
2087 /* Partial read of file from server */
2088 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2089 {
2090         register size_t len = 0;
2091         char aaa[SIZ];
2092
2093         if (!buf) return 0;
2094         if (!cret) return 0;
2095         if (bytes < 1) return 0;
2096         if (offset < 0) return 0;
2097
2098         CtdlIPC_lock(ipc);
2099         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2100         CtdlIPC_putline(ipc, aaa);
2101         CtdlIPC_getline(ipc, aaa);
2102         if (aaa[0] != '6')
2103                 strcpy(cret, &aaa[4]);
2104         else {
2105                 len = extract_long(&aaa[4], 0);
2106                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2107                 if (*buf) {
2108                         /* I know what I'm doing */
2109                         serv_read(ipc, (*buf + offset), len);
2110                 } else {
2111                         /* We have to read regardless */
2112                         serv_read(ipc, aaa, len);
2113                         len = 0;
2114                 }
2115         }
2116         CtdlIPC_unlock(ipc);
2117         return len;
2118 }
2119
2120
2121 /* CLOS */
2122 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2123 {
2124         register int ret;
2125
2126         if (!cret) return -2;
2127         if (!ipc->downloading) return -2;
2128
2129         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2130         if (ret / 100 == 2)
2131                 ipc->downloading = 0;
2132         return ret;
2133 }
2134
2135
2136 /* MSGP */
2137 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2138         register int ret;
2139         char cmd[SIZ];
2140         
2141         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2142         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2143         return ret;
2144 }
2145
2146
2147
2148 /* READ */
2149 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2150                void (*progress_gauge_callback)(unsigned long, unsigned long),
2151                char *cret)
2152 {
2153         register size_t len;
2154
2155         if (!cret) return -1;
2156         if (!buf) return -1;
2157         if (*buf) return -1;
2158         if (!ipc->downloading) return -1;
2159
2160         len = resume;
2161         if (progress_gauge_callback)
2162                 progress_gauge_callback(len, bytes);
2163         while (len < bytes) {
2164                 register size_t block;
2165
2166                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2167                 if (block == 0) {
2168                         free(*buf);
2169                         return 0;
2170                 }
2171                 len += block;
2172                 if (progress_gauge_callback)
2173                         progress_gauge_callback(len, bytes);
2174         }
2175         return len;
2176 }
2177
2178
2179 /* READ - pipelined */
2180 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2181                size_t resume,
2182                void (*progress_gauge_callback)(unsigned long, unsigned long),
2183                char *cret)
2184 {
2185         register size_t len;
2186         register int calls;     /* How many calls in the pipeline */
2187         register int i;         /* iterator */
2188         char aaa[4096];
2189
2190         if (!cret) return -1;
2191         if (!buf) return -1;
2192         if (*buf) return -1;
2193         if (!ipc->downloading) return -1;
2194
2195         *buf = (void *)realloc(*buf, bytes - resume);
2196         if (!*buf) return -1;
2197
2198         len = 0;
2199         CtdlIPC_lock(ipc);
2200         if (progress_gauge_callback)
2201                 progress_gauge_callback(len, bytes);
2202
2203         /* How many calls will be in the pipeline? */
2204         calls = (bytes - resume) / 4096;
2205         if ((bytes - resume) % 4096) calls++;
2206
2207         /* Send all requests at once */
2208         for (i = 0; i < calls; i++) {
2209                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2210                 CtdlIPC_putline(ipc, aaa);
2211         }
2212
2213         /* Receive all responses at once */
2214         for (i = 0; i < calls; i++) {
2215                 CtdlIPC_getline(ipc, aaa);
2216                 if (aaa[0] != '6')
2217                         strcpy(cret, &aaa[4]);
2218                 else {
2219                         len = extract_long(&aaa[4], 0);
2220                         /* I know what I'm doing */
2221                         serv_read(ipc, ((*buf) + (i * 4096)), len);
2222                 }
2223                 if (progress_gauge_callback)
2224                         progress_gauge_callback(i * 4096 + len, bytes);
2225         }
2226         CtdlIPC_unlock(ipc);
2227         return len;
2228 }
2229
2230
2231 /* UCLS */
2232 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2233 {
2234         register int ret;
2235         char cmd[8];
2236
2237         if (!cret) return -1;
2238         if (!ipc->uploading) return -1;
2239
2240         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2241         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2242         ipc->uploading = 0;
2243         return ret;
2244 }
2245
2246
2247 /* WRIT */
2248 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2249                 void (*progress_gauge_callback)(unsigned long, unsigned long),
2250                 char *cret)
2251 {
2252         register int ret = -1;
2253         register size_t offset = 0;
2254         size_t bytes;
2255         char aaa[SIZ];
2256         char buf[4096];
2257         FILE *fd;
2258
2259         if (!cret) return -1;
2260         if (!path) return -1;
2261         if (!*path) return -1;
2262
2263         fd = fopen(path, "r");
2264         if (!fd) return -2;
2265
2266         fseek(fd, 0L, SEEK_END);
2267         bytes = ftell(fd);
2268         rewind(fd);
2269
2270         if (progress_gauge_callback)
2271                 progress_gauge_callback(0, bytes);
2272
2273         while (offset < bytes) {
2274                 register size_t to_write;
2275
2276                 /* Read some data in */
2277                 to_write = fread(buf, 1, 4096, fd);
2278                 if (!to_write) {
2279                         if (feof(fd) || ferror(fd)) break;
2280                 }
2281                 sprintf(aaa, "WRIT %d", (int)to_write);
2282                 CtdlIPC_putline(ipc, aaa);
2283                 CtdlIPC_getline(ipc, aaa);
2284                 strcpy(cret, &aaa[4]);
2285                 ret = atoi(aaa);
2286                 if (aaa[0] == '7') {
2287                         to_write = extract_long(&aaa[4], 0);
2288                         
2289                         serv_write(ipc, buf, to_write);
2290                         offset += to_write;
2291                         if (progress_gauge_callback)
2292                                 progress_gauge_callback(offset, bytes);
2293                         /* Detect short reads and back up if needed */
2294                         /* offset will never be negative anyway */
2295                         fseek(fd, (signed)offset, SEEK_SET);
2296                 } else {
2297                         break;
2298                 }
2299         }
2300         if (progress_gauge_callback)
2301                 progress_gauge_callback(1, 1);
2302         return (!ferror(fd) ? ret : -2);
2303 }
2304
2305
2306 /*
2307  * Generic command method.  This method should handle any server command
2308  * except for CHAT.  It takes the following arguments:
2309  *
2310  * ipc                  The server to speak with
2311  * command              Preformatted command to send to server
2312  * to_send              A text or binary file to send to server
2313  *                      (only sent if server requests it)
2314  * bytes_to_send        The number of bytes in to_send (required if
2315  *                      sending binary, optional if sending listing)
2316  * to_receive           Pointer to a NULL pointer, if the server
2317  *                      sends text or binary we will allocate memory
2318  *                      for the file and stuff it here
2319  * bytes_to_receive     If a file is received, we will store its
2320  *                      byte count here
2321  * proto_response       The protocol response.  Caller must provide
2322  *                      this buffer and ensure that it is at least
2323  *                      128 bytes in length.
2324  *
2325  * This function returns a number equal to the protocol response number,
2326  * -1 if an internal error occurred, -2 if caller provided bad values,
2327  * or 0 - the protocol response number if bad values were found during
2328  * the protocol exchange.
2329  * It stores the protocol response string (minus the number) in 
2330  * protocol_response as described above.  Some commands send additional
2331  * data in this string.
2332  */
2333 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2334                 const char *command, const char *to_send,
2335                 size_t bytes_to_send, char **to_receive, 
2336                 size_t *bytes_to_receive, char *proto_response)
2337 {
2338         char buf[SIZ];
2339         register int ret;
2340         int watch_ssl = 0;
2341
2342         if (!command) return -2;
2343         if (!proto_response) return -2;
2344
2345 #ifdef HAVE_OPENSSL
2346         if (ipc->ssl) watch_ssl = 1;
2347 #endif
2348
2349         CtdlIPC_lock(ipc);
2350         CtdlIPC_putline(ipc, command);
2351         while (1) {
2352                 CtdlIPC_getline(ipc, proto_response);
2353                 if (proto_response[3] == '*')
2354                         express_msgs = 1;
2355                 ret = atoi(proto_response);
2356                 strcpy(proto_response, &proto_response[4]);
2357                 switch (ret / 100) {
2358                 default:                        /* Unknown, punt */
2359                 case 2:                         /* OK */
2360                 case 3:                         /* MORE_DATA */
2361                 case 5:                         /* ERROR */
2362                         /* Don't need to do anything */
2363                         break;
2364                 case 1:                         /* LISTING_FOLLOWS */
2365                         if (to_receive && !*to_receive && bytes_to_receive) {
2366                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2367                         } else { /* Drain */
2368                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2369                                 ret = -ret;
2370                         }
2371                         break;
2372                 case 4:                         /* SEND_LISTING */
2373                         if (to_send) {
2374                                 CtdlIPCSendListing(ipc, to_send);
2375                         } else {
2376                                 /* No listing given, fake it */
2377                                 CtdlIPC_putline(ipc, "000");
2378                                 ret = -ret;
2379                         }
2380                         break;
2381                 case 6:                         /* BINARY_FOLLOWS */
2382                         if (to_receive && !*to_receive && bytes_to_receive) {
2383                                 *bytes_to_receive =
2384                                         extract_long(proto_response, 0);
2385                                 *to_receive = (char *)
2386                                         malloc((size_t)*bytes_to_receive);
2387                                 if (!*to_receive) {
2388                                         ret = -1;
2389                                 } else {
2390                                         serv_read(ipc, *to_receive,
2391                                                         *bytes_to_receive);
2392                                 }
2393                         } else {
2394                                 /* Drain */
2395                                 size_t drain;
2396
2397                                 drain = extract_long(proto_response, 0);
2398                                 while (drain > SIZ) {
2399                                         serv_read(ipc, buf, SIZ);
2400                                         drain -= SIZ;
2401                                 }
2402                                 serv_read(ipc, buf, drain);
2403                                 ret = -ret;
2404                         }
2405                         break;
2406                 case 7:                         /* SEND_BINARY */
2407                         if (to_send && bytes_to_send) {
2408                                 serv_write(ipc, to_send, bytes_to_send);
2409                         } else if (bytes_to_send) {
2410                                 /* Fake it, send nulls */
2411                                 size_t fake;
2412
2413                                 fake = bytes_to_send;
2414                                 memset(buf, '\0', SIZ);
2415                                 while (fake > SIZ) {
2416                                         serv_write(ipc, buf, SIZ);
2417                                         fake -= SIZ;
2418                                 }
2419                                 serv_write(ipc, buf, fake);
2420                                 ret = -ret;
2421                         } /* else who knows?  DANGER WILL ROBINSON */
2422                         break;
2423                 case 8:                         /* START_CHAT_MODE */
2424                         if (!strncasecmp(command, "CHAT", 4)) {
2425                                 /* Don't call chatmode with generic! */
2426                                 CtdlIPC_putline(ipc, "/quit");
2427                                 ret = -ret;
2428                         } else {
2429                                 /* In this mode we send then receive listing */
2430                                 if (to_send) {
2431                                         CtdlIPCSendListing(ipc, to_send);
2432                                 } else {
2433                                         /* No listing given, fake it */
2434                                         CtdlIPC_putline(ipc, "000");
2435                                         ret = -ret;
2436                                 }
2437                                 if (to_receive && !*to_receive
2438                                                 && bytes_to_receive) {
2439                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2440                                 } else { /* Drain */
2441                                         while (CtdlIPC_getline(ipc, buf),
2442                                                         strcmp(buf, "000")) ;
2443                                         ret = -ret;
2444                                 }
2445                         }
2446                         break;
2447                 case 9:                         /* ASYNC_MSG */
2448                         /* CtdlIPCDoAsync(ret, proto_response); */
2449                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2450                         break;
2451                 }
2452                 if (ret / 100 != 9)
2453                         break;
2454         }
2455         CtdlIPC_unlock(ipc);
2456         return ret;
2457 }
2458
2459
2460 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2461 {
2462         struct hostent *phe;
2463         struct servent *pse;
2464         struct protoent *ppe;
2465         struct sockaddr_in sin;
2466         int s, type;
2467
2468         memset(&sin, 0, sizeof(sin));
2469         sin.sin_family = AF_INET;
2470
2471         pse = getservbyname(service, protocol);
2472         if (pse != NULL) {
2473                 sin.sin_port = pse->s_port;
2474         }
2475         else if (atoi(service) > 0) {
2476                 sin.sin_port = htons(atoi(service));
2477         }
2478         else {
2479                 sin.sin_port = htons(defaultPort);
2480         }
2481         phe = gethostbyname(host);
2482         if (phe) {
2483                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2484         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2485                 return -1;
2486         }
2487         if ((ppe = getprotobyname(protocol)) == 0) {
2488                 return -1;
2489         }
2490         if (!strcmp(protocol, "udp")) {
2491                 type = SOCK_DGRAM;
2492         } else {
2493                 type = SOCK_STREAM;
2494         }
2495
2496         s = socket(PF_INET, type, ppe->p_proto);
2497         if (s < 0) {
2498                 return -1;
2499         }
2500
2501         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2502                 return -1;
2503         }
2504
2505         return (s);
2506 }
2507
2508 static int uds_connectsock(int *isLocal, char *sockpath)
2509 {
2510         struct sockaddr_un addr;
2511         int s;
2512
2513         memset(&addr, 0, sizeof(addr));
2514         addr.sun_family = AF_UNIX;
2515         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2516
2517         s = socket(AF_UNIX, SOCK_STREAM, 0);
2518         if (s < 0) {
2519                 return -1;
2520         }
2521
2522         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2523                 return -1;
2524         }
2525
2526         *isLocal = 1;
2527         return s;
2528 }
2529
2530
2531 /*
2532  * input binary data from socket
2533  */
2534 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2535 {
2536         unsigned int len, rlen;
2537
2538 #if defined(HAVE_OPENSSL)
2539         if (ipc->ssl) {
2540                 serv_read_ssl(ipc, buf, bytes);
2541                 return;
2542         }
2543 #endif
2544         len = 0;
2545         while (len < bytes) {
2546                 rlen = read(ipc->sock, &buf[len], bytes - len);
2547                 if (rlen < 1) {
2548                         connection_died(ipc);
2549                         return;
2550                 }
2551                 len += rlen;
2552         }
2553 }
2554
2555
2556 /*
2557  * send binary to server
2558  */
2559 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2560 {
2561         unsigned int bytes_written = 0;
2562         int retval;
2563
2564 #if defined(HAVE_OPENSSL)
2565         if (ipc->ssl) {
2566                 serv_write_ssl(ipc, buf, nbytes);
2567                 return;
2568         }
2569 #endif
2570         while (bytes_written < nbytes) {
2571                 retval = write(ipc->sock, &buf[bytes_written],
2572                                nbytes - bytes_written);
2573                 if (retval < 1) {
2574                         connection_died(ipc);
2575                         return;
2576                 }
2577                 bytes_written += retval;
2578         }
2579 }
2580
2581
2582 #ifdef HAVE_OPENSSL
2583 /*
2584  * input binary data from encrypted connection
2585  */
2586 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2587 {
2588         int len, rlen;
2589         char junk[1];
2590
2591         len = 0;
2592         while (len < bytes) {
2593                 if (SSL_want_read(ipc->ssl)) {
2594                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2595                                 error_printf("SSL_write in serv_read:\n");
2596                                 ERR_print_errors_fp(stderr);
2597                         }
2598                 }
2599                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2600                 if (rlen < 1) {
2601                         long errval;
2602
2603                         errval = SSL_get_error(ipc->ssl, rlen);
2604                         if (errval == SSL_ERROR_WANT_READ ||
2605                                         errval == SSL_ERROR_WANT_WRITE) {
2606                                 sleep(1);
2607                                 continue;
2608                         }
2609                         if (errval == SSL_ERROR_ZERO_RETURN ||
2610                                         errval == SSL_ERROR_SSL) {
2611                                 serv_read(ipc, &buf[len], bytes - len);
2612                                 return;
2613                         }
2614                         error_printf("SSL_read in serv_read:\n");
2615                         ERR_print_errors_fp(stderr);
2616                         connection_died(NULL);
2617                         return;
2618                 }
2619                 len += rlen;
2620         }
2621 }
2622
2623
2624 /*
2625  * send binary to server encrypted
2626  */
2627 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2628 {
2629         unsigned int bytes_written = 0;
2630         int retval;
2631         char junk[1];
2632
2633         while (bytes_written < nbytes) {
2634                 if (SSL_want_write(ipc->ssl)) {
2635                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2636                                 error_printf("SSL_read in serv_write:\n");
2637                                 ERR_print_errors_fp(stderr);
2638                         }
2639                 }
2640                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2641                                 nbytes - bytes_written);
2642                 if (retval < 1) {
2643                         long errval;
2644
2645                         errval = SSL_get_error(ipc->ssl, retval);
2646                         if (errval == SSL_ERROR_WANT_READ ||
2647                                         errval == SSL_ERROR_WANT_WRITE) {
2648                                 sleep(1);
2649                                 continue;
2650                         }
2651                         if (errval == SSL_ERROR_ZERO_RETURN ||
2652                                         errval == SSL_ERROR_SSL) {
2653                                 serv_write(ipc, &buf[bytes_written],
2654                                                 nbytes - bytes_written);
2655                                 return;
2656                         }
2657                         error_printf("SSL_write in serv_write:\n");
2658                         ERR_print_errors_fp(stderr);
2659                         connection_died(NULL);
2660                         return;
2661                 }
2662                 bytes_written += retval;
2663         }
2664 }
2665
2666
2667 static void CtdlIPC_init_OpenSSL(void)
2668 {
2669         int a;
2670         SSL_METHOD *ssl_method;
2671         DH *dh;
2672         
2673         /* already done init */
2674         if (ssl_ctx) {
2675                 return;
2676         }
2677
2678         /* Get started */
2679         ssl_ctx = NULL;
2680         dh = NULL;
2681         SSL_load_error_strings();
2682         SSLeay_add_ssl_algorithms();
2683
2684         /* Set up the SSL context in which we will oeprate */
2685         ssl_method = SSLv23_client_method();
2686         ssl_ctx = SSL_CTX_new(ssl_method);
2687         if (!ssl_ctx) {
2688                 error_printf("SSL_CTX_new failed: %s\n",
2689                                 ERR_reason_error_string(ERR_get_error()));
2690                 return;
2691         }
2692         /* Any reasonable cipher we can get */
2693         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2694                 error_printf("No ciphers available for encryption\n");
2695                 return;
2696         }
2697         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2698         
2699         /* Load DH parameters into the context */
2700         dh = DH_new();
2701         if (!dh) {
2702                 error_printf("Can't allocate a DH object: %s\n",
2703                                 ERR_reason_error_string(ERR_get_error()));
2704                 return;
2705         }
2706         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2707                 error_printf("Can't assign DH_P: %s\n",
2708                                 ERR_reason_error_string(ERR_get_error()));
2709                 DH_free(dh);
2710                 return;
2711         }
2712         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2713                 error_printf("Can't assign DH_G: %s\n",
2714                                 ERR_reason_error_string(ERR_get_error()));
2715                 DH_free(dh);
2716                 return;
2717         }
2718         dh->length = DH_L;
2719         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2720         DH_free(dh);
2721
2722 #ifdef THREADED_CLIENT
2723         /* OpenSSL requires callbacks for threaded clients */
2724         CRYPTO_set_locking_callback(ssl_lock);
2725         CRYPTO_set_id_callback(id_callback);
2726
2727         /* OpenSSL requires us to do semaphores for threaded clients */
2728         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2729         if (!Critters) {
2730                 perror("malloc failed");
2731                 exit(1);
2732         } else {
2733                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2734                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2735                         if (!Critters[a]) {
2736                                 perror("malloc failed");
2737                                 exit(1);
2738                         }
2739                         pthread_mutex_init(Critters[a], NULL);
2740                 }
2741         }
2742 #endif /* THREADED_CLIENT */       
2743 }
2744
2745
2746 static void ssl_lock(int mode, int n, const char *file, int line)
2747 {
2748 #ifdef THREADED_CLIENT
2749         if (mode & CRYPTO_LOCK)
2750                 pthread_mutex_lock(Critters[n]);
2751         else
2752                 pthread_mutex_unlock(Critters[n]);
2753 #endif /* THREADED_CLIENT */
2754 }
2755
2756 #ifdef THREADED_CLIENT
2757 static unsigned long id_callback(void) {
2758         return (unsigned long)pthread_self();
2759 }
2760 #endif /* THREADED_CLIENT */
2761 #endif /* HAVE_OPENSSL */
2762
2763
2764 /*
2765  * input string from socket - implemented in terms of serv_read()
2766  */
2767 void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2768 {
2769         int i;
2770
2771         /* Read one character at a time. */
2772         for (i = 0;; i++) {
2773                 serv_read(ipc, &buf[i], 1);
2774                 if (buf[i] == '\n' || i == (SIZ-1))
2775                         break;
2776         }
2777
2778         /* If we got a long line, discard characters until the newline. */
2779         if (i == (SIZ-1))
2780                 while (buf[i] != '\n')
2781                         serv_read(ipc, &buf[i], 1);
2782
2783         /* Strip the trailing newline.
2784          */
2785         buf[i] = 0;
2786 }
2787
2788
2789 /*
2790  * send line to server - implemented in terms of serv_write()
2791  */
2792 void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2793 {
2794         /* error_printf("< %s\n", buf); */
2795         serv_write(ipc, buf, strlen(buf));
2796         serv_write(ipc, "\n", 1);
2797
2798         ipc->last_command_sent = time(NULL);
2799 }
2800
2801
2802 /*
2803  * attach to server
2804  */
2805 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2806 {
2807         int a;
2808         char cithost[SIZ];
2809         char citport[SIZ];
2810         char sockpath[SIZ];
2811
2812         CtdlIPC *ipc = ialloc(CtdlIPC);
2813         if (!ipc) {
2814                 return 0;
2815         }
2816 #if defined(HAVE_OPENSSL)
2817         ipc->ssl = NULL;
2818         CtdlIPC_init_OpenSSL();
2819 #endif
2820 #if defined(HAVE_PTHREAD_H)
2821         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2822 #endif
2823         ipc->sock = -1;                 /* Not connected */
2824         ipc->isLocal = 0;               /* Not local, of course! */
2825         ipc->downloading = 0;
2826         ipc->uploading = 0;
2827         ipc->last_command_sent = 0L;
2828         ipc->network_status_cb = NULL;
2829
2830         strcpy(cithost, DEFAULT_HOST);  /* default host */
2831         strcpy(citport, DEFAULT_PORT);  /* default port */
2832
2833         for (a = 0; a < argc; ++a) {
2834                 if (a == 0) {
2835                         /* do nothing */
2836                 } else if (a == 1) {
2837                         strcpy(cithost, argv[a]);
2838                 } else if (a == 2) {
2839                         strcpy(citport, argv[a]);
2840                 } else {
2841                         error_printf("%s: usage: ",argv[0]);
2842                         error_printf("%s [host] [port] ",argv[0]);
2843                         ifree(ipc);
2844                         errno = EINVAL;
2845                         return 0;
2846                 }
2847         }
2848
2849         if ((!strcmp(cithost, "localhost"))
2850            || (!strcmp(cithost, "127.0.0.1"))) {
2851                 ipc->isLocal = 1;
2852         }
2853
2854         /* If we're using a unix domain socket we can do a bunch of stuff */
2855         if (!strcmp(cithost, UDS)) {
2856                 snprintf(sockpath, sizeof sockpath, BBSDIR "/citadel.socket");
2857                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2858                 if (ipc->sock == -1) {
2859                         ifree(ipc);
2860                         return 0;
2861                 }
2862                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2863                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2864                 return ipc;
2865         }
2866
2867         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2868         if (ipc->sock == -1) {
2869                 ifree(ipc);
2870                 return 0;
2871         }
2872         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2873         if (portbuf != NULL) strcpy(portbuf, citport);
2874         return ipc;
2875 }
2876
2877 /*
2878  * return the file descriptor of the server socket so we can select() on it.
2879  *
2880  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2881  * rewritten...
2882  */
2883 int CtdlIPC_getsockfd(CtdlIPC* ipc)
2884 {
2885         return ipc->sock;
2886 }
2887
2888
2889 /*
2890  * return one character
2891  *
2892  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2893  * rewritten...
2894  */
2895 char CtdlIPC_get(CtdlIPC* ipc)
2896 {
2897         char buf[2];
2898         char ch;
2899
2900         serv_read(ipc, buf, 1);
2901         ch = (int) buf[0];
2902
2903         return (ch);
2904 }