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