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