source: functions.c @ f1fc47f

barnowl_perlaimdebianrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since f1fc47f was 13a3c1db, checked in by Nelson Elhage <nelhage@mit.edu>, 18 years ago
Refactoring message processing: All new messages, incoming or outgoing, get added to the owl_global_messagequeue by protocol code, and then passed to owl_proces_message, which serves as a single central code path for adding messages to the message list.
  • Property mode set to 100644
File size: 99.3 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <signal.h>
5#include <netinet/in.h>
6#include <string.h>
7#include <time.h>
8#include <sys/types.h>
9#include <sys/stat.h>
10#include <sys/wait.h>
11#include <errno.h>
12#include <signal.h>
13#include "owl.h"
14
15static const char fileIdent[] = "$Id$";
16
17void owl_function_noop(void)
18{
19  return;
20}
21
22char *owl_function_command(char *cmdbuff)
23{
24  owl_function_debugmsg("executing command: %s", cmdbuff);
25  return owl_cmddict_execute(owl_global_get_cmddict(&g), 
26                             owl_global_get_context(&g), cmdbuff);
27}
28
29void owl_function_command_norv(char *cmdbuff)
30{
31  char *rv;
32  rv=owl_function_command(cmdbuff);
33  if (rv) owl_free(rv);
34}
35
36void owl_function_command_alias(char *alias_from, char *alias_to)
37{
38  owl_cmddict_add_alias(owl_global_get_cmddict(&g), alias_from, alias_to);
39}
40
41owl_cmd *owl_function_get_cmd(char *name)
42{
43  return owl_cmddict_find(owl_global_get_cmddict(&g), name);
44}
45
46void owl_function_show_commands()
47{
48  owl_list l;
49  owl_fmtext fm;
50
51  owl_fmtext_init_null(&fm);
52  owl_fmtext_append_bold(&fm, "Commands:  ");
53  owl_fmtext_append_normal(&fm, "(use 'show command <name>' for details)\n");
54  owl_cmddict_get_names(owl_global_get_cmddict(&g), &l);
55  owl_fmtext_append_list(&fm, &l, "\n", owl_function_cmd_describe);
56  owl_fmtext_append_normal(&fm, "\n");
57  owl_function_popless_fmtext(&fm);
58  owl_cmddict_namelist_free(&l);
59  owl_fmtext_free(&fm);
60}
61
62void owl_function_show_view(char *viewname)
63{
64  owl_view *v;
65  owl_fmtext fm;
66
67  /* we only have the one view right now */
68  v=owl_global_get_current_view(&g);
69  if (viewname && strcmp(viewname, owl_view_get_name(v))) {
70    owl_function_error("No view named '%s'", viewname);
71    return;
72  }
73
74  owl_fmtext_init_null(&fm);
75  owl_view_to_fmtext(v, &fm);
76  owl_function_popless_fmtext(&fm);
77  owl_fmtext_free(&fm);
78}
79
80void owl_function_show_styles() {
81  owl_list l;
82  owl_fmtext fm;
83
84  owl_fmtext_init_null(&fm);
85  owl_fmtext_append_bold(&fm, "Styles:\n");
86  owl_global_get_style_names(&g, &l);
87  owl_fmtext_append_list(&fm, &l, "\n", owl_function_style_describe);
88  owl_fmtext_append_normal(&fm, "\n");
89  owl_function_popless_fmtext(&fm);
90  owl_list_free_all(&l, owl_free);
91  owl_fmtext_free(&fm);
92}
93
94char *owl_function_style_describe(void *name) {
95  char *desc, *s;
96  owl_style *style;
97  style = owl_global_get_style_by_name(&g, name);
98  if (style) {
99    desc = owl_style_get_description(style);
100  } else {
101    desc = "???";
102  }
103  s = owl_sprintf("%-20s - %s%s", name, 
104                  0==owl_style_validate(style)?"":"[INVALID] ",
105                  desc);
106  return s;
107}
108
109char *owl_function_cmd_describe(void *name)
110{
111  owl_cmd *cmd = owl_cmddict_find(owl_global_get_cmddict(&g), name);
112  if (cmd) return owl_cmd_describe(cmd);
113  else return(NULL);
114}
115
116void owl_function_show_command(char *name)
117{
118  owl_function_help_for_command(name);
119}
120
121void owl_function_show_license()
122{
123  char *text;
124
125  text=""
126    "barnowl version " OWL_VERSION_STRING "\n"
127    "Copyright (c) 2004 James Kretchmar. All rights reserved.\n"
128    "\n"
129    "Redistribution and use in source and binary forms, with or without\n"
130    "modification, are permitted provided that the following conditions are\n"
131    "met:\n"
132    "\n"
133    "   * Redistributions of source code must retain the above copyright\n"
134    "     notice, this list of conditions and the following disclaimer.\n"
135    "\n"
136    "   * Redistributions in binary form must reproduce the above copyright\n"
137    "     notice, this list of conditions and the following disclaimer in\n"
138    "     the documentation and/or other materials provided with the\n"
139    "     distribution.\n"
140    "\n"
141    "   * Redistributions in any form must be accompanied by information on\n"
142    "     how to obtain complete source code for the Owl software and any\n"
143    "     accompanying software that uses the Owl software. The source code\n"
144    "     must either be included in the distribution or be available for no\n"
145    "     more than the cost of distribution plus a nominal fee, and must be\n"
146    "     freely redistributable under reasonable conditions. For an\n"
147    "     executable file, complete source code means the source code for\n"
148    "     all modules it contains. It does not include source code for\n"
149    "     modules or files that typically accompany the major components of\n"
150    "     the operating system on which the executable file runs.\n"
151    "\n"
152    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
153    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
154    "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR\n"
155    "NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE\n"
156    "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
157    "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
158    "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\n"
159    "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n"
160    "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\n"
161    "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN\n"
162    "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n";
163  owl_function_popless_text(text);
164}
165
166/* Create an admin message, append it to the global list of messages
167 * and redisplay if necessary.
168 */
169void owl_function_adminmsg(char *header, char *body)
170{
171  owl_message *m;
172
173  m=owl_malloc(sizeof(owl_message));
174  owl_message_create_admin(m, header, body);
175 
176  /* add it to the global list and current view */
177  owl_messagelist_append_element(owl_global_get_msglist(&g), m);
178  owl_view_consider_message(owl_global_get_current_view(&g), m);
179
180  /* do followlast if necessary */
181  if (owl_global_should_followlast(&g)) owl_function_lastmsg_noredisplay();
182
183  /* redisplay etc. */
184  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
185  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
186    owl_popwin_refresh(owl_global_get_popwin(&g));
187  }
188  wnoutrefresh(owl_global_get_curs_recwin(&g));
189  owl_global_set_needrefresh(&g);
190}
191
192/* Create an outgoing zephyr message and return a pointer to it.  Does
193 * not put it on the global queue, use owl_function_add_message() for
194 * that.
195 */
196owl_message *owl_function_make_outgoing_zephyr(char *body, char *zwriteline, char *zsig)
197{
198  owl_message *m;
199  owl_zwrite z;
200 
201  /* create a zwrite for the purpose of filling in other message fields */
202  owl_zwrite_create_from_line(&z, zwriteline);
203
204  /* create the message */
205  m=owl_malloc(sizeof(owl_message));
206  owl_message_create_from_zwriteline(m, zwriteline, body, zsig);
207  owl_zwrite_free(&z);
208
209  return(m);
210}
211
212/* Create an outgoing AIM message, returns a pointer to the created
213 * message or NULL if we're not logged into AIM (and thus unable to
214 * create the message).  Does not put it on the global queue.  Use
215 * owl_function_add_message() for that .
216 */
217owl_message *owl_function_make_outgoing_aim(char *body, char *to)
218{
219  owl_message *m;
220
221  /* error if we're not logged into aim */
222  if (!owl_global_is_aimloggedin(&g)) return(NULL);
223 
224  m=owl_malloc(sizeof(owl_message));
225  owl_message_create_aim(m,
226                         owl_global_get_aim_screenname(&g),
227                         to,
228                         body,
229                         OWL_MESSAGE_DIRECTION_OUT,
230                         0);
231  return(m);
232}
233
234/* Create an outgoing loopback message and return a pointer to it.
235 * Does not append it to the global queue, use
236 * owl_function_add_message() for that.
237 */
238owl_message *owl_function_make_outgoing_loopback(char *body)
239{
240  owl_message *m;
241
242  /* create the message */
243  m=owl_malloc(sizeof(owl_message));
244  owl_message_create_loopback(m, body);
245  owl_message_set_direction_out(m);
246
247  return(m);
248}
249
250void owl_function_zwrite_setup(char *line)
251{
252  owl_editwin *e;
253  char buff[1024];
254  owl_zwrite z;
255  int ret;
256
257  /* check the arguments */
258  ret=owl_zwrite_create_from_line(&z, line);
259  if (ret) {
260    owl_function_error("Error in zwrite arugments");
261    owl_zwrite_free(&z);
262    return;
263  }
264
265  /* send a ping if necessary */
266  if (owl_global_is_txping(&g)) {
267    owl_zwrite_send_ping(&z);
268  }
269  owl_zwrite_free(&z);
270
271  /* create and setup the editwin */
272  e=owl_global_get_typwin(&g);
273  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
274
275  if (!owl_global_get_lockout_ctrld(&g)) {
276    owl_function_makemsg("Type your zephyr below.  End with ^D or a dot on a line by itself.  ^C will quit.");
277  } else {
278    owl_function_makemsg("Type your zephyr below.  End with a dot on a line by itself.  ^C will quit.");
279  }
280
281  owl_editwin_clear(e);
282  owl_editwin_set_dotsend(e);
283  strcpy(buff, "----> ");
284  strcat(buff, line);
285  strcat(buff, "\n");
286  owl_editwin_set_locktext(e, buff);
287
288  /* make it active */
289  owl_global_set_typwin_active(&g);
290
291  owl_global_set_buffercommand(&g, line);
292  owl_global_set_buffercallback(&g, &owl_callback_zwrite);
293}
294
295void owl_function_aimwrite_setup(char *line)
296{
297  owl_editwin *e;
298  char buff[1024];
299
300  /* check the arguments */
301
302  /* create and setup the editwin */
303  e=owl_global_get_typwin(&g);
304  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
305
306  if (!owl_global_get_lockout_ctrld(&g)) {
307    owl_function_makemsg("Type your message below.  End with ^D or a dot on a line by itself.  ^C will quit.");
308  } else {
309    owl_function_makemsg("Type your message below.  End with a dot on a line by itself.  ^C will quit.");
310  }
311
312  owl_editwin_clear(e);
313  owl_editwin_set_dotsend(e);
314  strcpy(buff, "----> ");
315  strcat(buff, line);
316  strcat(buff, "\n");
317  owl_editwin_set_locktext(e, buff);
318
319  /* make it active */
320  owl_global_set_typwin_active(&g);
321
322  owl_global_set_buffercommand(&g, line);
323  owl_global_set_buffercallback(&g, &owl_callback_aimwrite);
324}
325
326void owl_function_loopwrite_setup()
327{
328  owl_editwin *e;
329
330  /* create and setup the editwin */
331  e=owl_global_get_typwin(&g);
332  owl_editwin_new_style(e, OWL_EDITWIN_STYLE_MULTILINE, owl_global_get_msg_history(&g));
333
334  if (!owl_global_get_lockout_ctrld(&g)) {
335    owl_function_makemsg("Type your message below.  End with ^D or a dot on a line by itself.  ^C will quit.");
336  } else {
337    owl_function_makemsg("Type your message below.  End with a dot on a line by itself.  ^C will quit.");
338  }
339
340  owl_editwin_clear(e);
341  owl_editwin_set_dotsend(e);
342  owl_editwin_set_locktext(e, "----> loopwrite\n");
343
344  /* make it active */
345  owl_global_set_typwin_active(&g);
346
347  owl_global_set_buffercommand(&g, "loopwrite");
348  owl_global_set_buffercallback(&g, &owl_callback_loopwrite);
349}
350
351void owl_callback_zwrite(owl_editwin *e) {
352  owl_function_zwrite(owl_editwin_get_command(e),
353                      owl_editwin_get_text(e));
354}
355
356/* send, log and display an outgoing zephyr.  If 'msg' is NULL
357 * the message is expected to be set from the zwrite line itself
358 */
359void owl_function_zwrite(char *line, char *msg)
360{
361  owl_zwrite z;
362  char *mymsg;
363  owl_message *m;
364
365  if(!strncmp(line, "zcrypt", strlen("zcrypt"))) {
366    owl_function_zcrypt(line, msg);
367    return;
368  }
369
370  /* create the zwrite and send the message */
371  owl_zwrite_create_from_line(&z, line);
372  if (msg) {
373    owl_zwrite_set_message(&z, msg);
374  }
375  owl_zwrite_send_message(&z);
376  owl_function_makemsg("Waiting for ack...");
377
378  /* If it's personal */
379  if (owl_zwrite_is_personal(&z)) {
380    /* create the outgoing message */
381    mymsg=owl_zwrite_get_message(&z);
382    m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z));
383
384    if (m) {
385      owl_global_messagequeue_addmsg(&g, m);
386    } else {
387      owl_function_error("Could not create outgoing zephyr message");
388    }
389  }
390
391  /* free the zwrite */
392  owl_zwrite_free(&z);
393}
394
395/* send, log and display an outgoing zcrypt zephyr.  If 'msg' is NULL
396 * the message is expected to be set from the zwrite line itself
397 */
398void owl_function_zcrypt(char *line, char *msg)
399{
400  owl_zwrite z;
401  char *mymsg;
402  char *cryptmsg;
403  owl_message *m;
404#ifdef OWL_ENABLE_ZCRYPT
405  int ret;
406#endif
407
408  /* create the zwrite and send the message */
409  owl_zwrite_create_from_line(&z, line);
410  if (msg) {
411    owl_zwrite_set_message(&z, msg);
412  }
413
414  mymsg=owl_zwrite_get_message(&z);
415#ifdef OWL_ENABLE_ZCRYPT
416  cryptmsg=owl_malloc(strlen(mymsg)*4);
417  ret=owl_zcrypt_encrypt(cryptmsg, mymsg, owl_zwrite_get_class(&z), owl_zwrite_get_instance(&z));
418  if (ret) {
419    owl_function_error("Error in zcrypt, possibly no key found.  Message not sent.");
420    owl_function_beep();
421    owl_free(cryptmsg);
422    return;
423  }
424#else
425  cryptmsg=owl_strdup(mymsg);
426#endif
427
428  owl_zwrite_set_message(&z, cryptmsg);
429  owl_zwrite_set_opcode(&z, "crypt");
430  mymsg=cryptmsg;
431   
432  owl_zwrite_send_message(&z);
433  owl_function_makemsg("Waiting for ack...");
434
435  /* If it's personal */
436  if (owl_zwrite_is_personal(&z)) {
437    /* create the outgoing message */
438    mymsg=owl_zwrite_get_message(&z);
439    m=owl_function_make_outgoing_zephyr(mymsg, line, owl_zwrite_get_zsig(&z));
440    if (m) {
441      owl_global_messagequeue_addmsg(&g, m);
442    } else {
443      owl_function_error("Could not create outgoing zephyr message");
444    }
445  }
446
447  /* free the zwrite */
448  owl_free(cryptmsg);
449  owl_zwrite_free(&z);
450}
451
452void owl_callback_aimwrite(owl_editwin *e) {
453  owl_function_aimwrite(owl_editwin_get_command(e),
454                        owl_editwin_get_text(e));
455}
456
457void owl_function_aimwrite(char *line, char *msg)
458{
459  int ret;
460  char *to, *format_msg;
461  owl_message *m;
462
463  to = line + 9;
464
465  /* make a formatted copy of the message */
466  format_msg=owl_strdup(msg);
467  owl_text_wordunwrap(format_msg);
468 
469  /* send the message */
470  ret=owl_aim_send_im(to, format_msg);
471  if (!ret) {
472    owl_function_makemsg("AIM message sent.");
473  } else {
474    owl_function_error("Could not send AIM message.");
475  }
476
477  /* create the outgoing message */
478  m=owl_function_make_outgoing_aim(msg, to);
479
480  if (m) {
481    owl_global_messagequeue_addmsg(&g, m);
482  } else {
483    owl_function_error("Could not create outgoing AIM message");
484  }
485
486  owl_free(format_msg);
487}
488
489void owl_function_send_aimawymsg(char *to, char *msg)
490{
491  int ret;
492  char *format_msg;
493  owl_message *m;
494
495  /* make a formatted copy of the message */
496  format_msg=owl_strdup(msg);
497  owl_text_wordunwrap(format_msg);
498 
499  /* send the message */
500  ret=owl_aim_send_awaymsg(to, format_msg);
501  if (!ret) {
502    /* owl_function_makemsg("AIM message sent."); */
503  } else {
504    owl_function_error("Could not send AIM message.");
505  }
506
507  /* create the message */
508  m=owl_function_make_outgoing_aim(msg, to);
509  if (m) {
510    owl_global_messagequeue_addmsg(&g, m);
511  } else {
512    owl_function_error("Could not create AIM message");
513  }
514  owl_free(format_msg);
515}
516
517void owl_callback_loopwrite(owl_editwin *e) {
518  owl_function_loopwrite(owl_editwin_get_text(e));
519}
520
521void owl_function_loopwrite(char *msg)
522{
523  owl_message *min, *mout;
524
525  /* create a message and put it on the message queue.  This simulates
526   * an incoming message */
527  min=owl_malloc(sizeof(owl_message));
528  mout=owl_function_make_outgoing_loopback(msg);
529
530  if (owl_global_is_displayoutgoing(&g)) {
531    owl_global_messagequeue_addmsg(&g, mout);
532  } else {
533    owl_message_free(mout);
534  }
535
536  owl_message_create_loopback(min, msg);
537  owl_message_set_direction_in(min);
538  owl_global_messagequeue_addmsg(&g, min);
539
540  /* fake a makemsg */
541  owl_function_makemsg("loopback message sent");
542}
543
544/* If filter is non-null, looks for the next message matching
545 * that filter.  If skip_deleted, skips any deleted messages.
546 * If last_if_none, will stop at the last message in the view
547 * if no matching messages are found.  */
548void owl_function_nextmsg_full(char *filter, int skip_deleted, int last_if_none)
549{
550  int curmsg, i, viewsize, found;
551  owl_view *v;
552  owl_filter *f = NULL;
553  owl_message *m;
554
555  v=owl_global_get_current_view(&g);
556
557  if (filter) {
558    f=owl_global_get_filter(&g, filter);
559    if (!f) {
560      owl_function_error("No %s filter defined", filter);
561      return;
562    }
563  }
564
565  curmsg=owl_global_get_curmsg(&g);
566  viewsize=owl_view_get_size(v);
567  found=0;
568
569  /* just check to make sure we're in bounds... */
570  if (curmsg>viewsize-1) curmsg=viewsize-1;
571  if (curmsg<0) curmsg=0;
572
573  for (i=curmsg+1; i<viewsize; i++) {
574    m=owl_view_get_element(v, i);
575    if (skip_deleted && owl_message_is_delete(m)) continue;
576    if (f && !owl_filter_message_match(f, m)) continue;
577    found = 1;
578    break;
579  }
580
581  if (i>owl_view_get_size(v)-1) i=owl_view_get_size(v)-1;
582
583  if (!found) {
584    owl_function_makemsg("already at last%s message%s%s",
585                         skip_deleted?" non-deleted":"",
586                         filter?" in ":"", filter?filter:"");
587    /* if (!skip_deleted) owl_function_beep(); */
588  }
589
590  if (last_if_none || found) {
591    owl_global_set_curmsg(&g, i);
592    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
593    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
594    owl_global_set_direction_downwards(&g);
595  }
596}
597
598void owl_function_prevmsg_full(char *filter, int skip_deleted, int first_if_none)
599{
600  int curmsg, i, viewsize, found;
601  owl_view *v;
602  owl_filter *f = NULL;
603  owl_message *m;
604
605  v=owl_global_get_current_view(&g);
606
607  if (filter) {
608    f=owl_global_get_filter(&g, filter);
609    if (!f) {
610      owl_function_error("No %s filter defined", filter);
611      return;
612    }
613  }
614
615  curmsg=owl_global_get_curmsg(&g);
616  viewsize=owl_view_get_size(v);
617  found=0;
618
619  /* just check to make sure we're in bounds... */
620  if (curmsg<0) curmsg=0;
621
622  for (i=curmsg-1; i>=0; i--) {
623    m=owl_view_get_element(v, i);
624    if (skip_deleted && owl_message_is_delete(m)) continue;
625    if (f && !owl_filter_message_match(f, m)) continue;
626    found = 1;
627    break;
628  }
629
630  if (i<0) i=0;
631
632  if (!found) {
633    owl_function_makemsg("already at first%s message%s%s",
634                         skip_deleted?" non-deleted":"",
635                         filter?" in ":"", filter?filter:"");
636    /* if (!skip_deleted) owl_function_beep(); */
637  }
638
639  if (first_if_none || found) {
640    owl_global_set_curmsg(&g, i);
641    owl_function_calculate_topmsg(OWL_DIRECTION_UPWARDS);
642    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
643    owl_global_set_direction_upwards(&g);
644  }
645}
646
647void owl_function_nextmsg()
648{
649  owl_function_nextmsg_full(NULL, 0, 1);
650}
651
652void owl_function_prevmsg()
653{
654  owl_function_prevmsg_full(NULL, 0, 1);
655}
656
657void owl_function_nextmsg_notdeleted()
658{
659  owl_function_nextmsg_full(NULL, 1, 1);
660}
661
662void owl_function_prevmsg_notdeleted()
663{
664  owl_function_prevmsg_full(NULL, 1, 1);
665}
666
667void owl_function_nextmsg_personal()
668{
669  owl_function_nextmsg_full("personal", 0, 0);
670}
671
672void owl_function_prevmsg_personal()
673{
674  owl_function_prevmsg_full("personal", 0, 0);
675}
676
677
678/* if move_after is 1, moves after the delete */
679void owl_function_deletecur(int move_after)
680{
681  int curmsg;
682  owl_view *v;
683
684  v=owl_global_get_current_view(&g);
685
686  /* bail if there's no current message */
687  if (owl_view_get_size(v) < 1) {
688    owl_function_error("No current message to delete");
689    return;
690  }
691
692  /* mark the message for deletion */
693  curmsg=owl_global_get_curmsg(&g);
694  owl_view_delete_element(v, curmsg);
695
696  if (move_after) {
697    /* move the poiner in the appropriate direction
698     * to the next undeleted msg */
699    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
700      owl_function_prevmsg_notdeleted();
701    } else {
702      owl_function_nextmsg_notdeleted();
703    }
704  }
705}
706
707void owl_function_undeletecur(int move_after)
708{
709  int curmsg;
710  owl_view *v;
711
712  v=owl_global_get_current_view(&g);
713 
714  if (owl_view_get_size(v) < 1) {
715    owl_function_error("No current message to undelete");
716    return;
717  }
718  curmsg=owl_global_get_curmsg(&g);
719
720  owl_view_undelete_element(v, curmsg);
721
722  if (move_after) {
723    if (owl_global_get_direction(&g)==OWL_DIRECTION_UPWARDS) {
724      if (curmsg>0) {
725        owl_function_prevmsg();
726      } else {
727        owl_function_nextmsg();
728      }
729    } else {
730      owl_function_nextmsg();
731    }
732  }
733
734  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
735}
736
737void owl_function_expunge()
738{
739  int curmsg;
740  owl_message *m;
741  owl_messagelist *ml;
742  owl_view *v;
743  int lastmsgid=0;
744
745  curmsg=owl_global_get_curmsg(&g);
746  v=owl_global_get_current_view(&g);
747  ml=owl_global_get_msglist(&g);
748
749  m=owl_view_get_element(v, curmsg);
750  if (m) lastmsgid = owl_message_get_id(m);
751
752  /* expunge the message list */
753  owl_messagelist_expunge(ml);
754
755  /* update all views (we only have one right now) */
756  owl_view_recalculate(v);
757
758  /* find where the new position should be
759     (as close as possible to where we last where) */
760  curmsg = owl_view_get_nearest_to_msgid(v, lastmsgid);
761  if (curmsg>owl_view_get_size(v)-1) curmsg = owl_view_get_size(v)-1;
762  if (curmsg<0) curmsg = 0;
763  owl_global_set_curmsg(&g, curmsg);
764  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
765  /* if there are no messages set the direction to down in case we
766     delete everything upwards */
767  owl_global_set_direction_downwards(&g);
768 
769  owl_function_makemsg("Messages expunged");
770  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
771}
772
773void owl_function_firstmsg()
774{
775  owl_global_set_curmsg(&g, 0);
776  owl_global_set_topmsg(&g, 0);
777  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
778  owl_global_set_direction_downwards(&g);
779}
780
781void owl_function_lastmsg_noredisplay()
782{
783  int oldcurmsg, curmsg;
784  owl_view *v;
785
786  v=owl_global_get_current_view(&g);
787  oldcurmsg=owl_global_get_curmsg(&g);
788  curmsg=owl_view_get_size(v)-1; 
789  if (curmsg<0) curmsg=0;
790  owl_global_set_curmsg(&g, curmsg);
791  if (oldcurmsg < curmsg) {
792    owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
793  } else if (curmsg<owl_view_get_size(v)) {
794    /* If already at the end, blank the screen and move curmsg
795     * past the end of the messages. */
796    owl_global_set_topmsg(&g, curmsg+1);
797    owl_global_set_curmsg(&g, curmsg+1);
798  } 
799  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
800  owl_global_set_direction_downwards(&g);
801}
802
803void owl_function_lastmsg()
804{
805  owl_function_lastmsg_noredisplay();
806  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
807}
808
809void owl_function_shift_right()
810{
811  owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
812  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
813  owl_global_set_needrefresh(&g);
814}
815
816void owl_function_shift_left()
817{
818  int shift;
819
820  shift=owl_global_get_rightshift(&g);
821  if (shift>=10) {
822    owl_global_set_rightshift(&g, shift-10);
823    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
824    owl_global_set_needrefresh(&g);
825  } else {
826    owl_function_beep();
827    owl_function_makemsg("Already full left");
828  }
829}
830
831void owl_function_unsuball()
832{
833  unsuball();
834  owl_function_makemsg("Unsubscribed from all messages.");
835}
836
837
838/* Load zephyr subscriptions from the named 'file' and load zephyr's
839 * default subscriptions as well.  An error message is printed if
840 * 'file' can't be opened or if zephyr reports an error in
841 * subscribing.
842 *
843 * If 'file' is NULL, this look for the default filename
844 * $HOME/.zephyr.subs.  If the file can not be opened in this case
845 * only, no error message is printed.
846 */
847void owl_function_loadsubs(char *file)
848{
849  int ret, ret2;
850  char *foo;
851
852  if (file==NULL) {
853    ret=owl_zephyr_loadsubs(NULL, 0);
854  } else {
855    ret=owl_zephyr_loadsubs(file, 1);
856  }
857
858  /* for backwards compatibility for now */
859  ret2=owl_zephyr_loaddefaultsubs();
860
861  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
862
863  foo=file?file:"file";
864  if (ret==0 && ret2==0) {
865    if (!file) {
866      owl_function_makemsg("Subscribed to messages.");
867    } else {
868      owl_function_makemsg("Subscribed to messages from %s", file);
869    }
870  } else if (ret==-1) {
871    owl_function_error("Could not read %s", foo);
872  } else {
873    owl_function_error("Error subscribing to messages");
874  }
875}
876
877void owl_function_loadloginsubs(char *file)
878{
879  int ret;
880
881  ret=owl_zephyr_loadloginsubs(file);
882
883  if (!owl_context_is_interactive(owl_global_get_context(&g))) return;
884  if (ret==0) {
885    owl_function_makemsg("Subscribed to login messages from file.");
886  } else if (ret==-1) {
887    owl_function_error("Could not open file for login subscriptions.");
888  } else {
889    owl_function_error("Error subscribing to login messages from file.");
890  }
891}
892
893void owl_callback_aimlogin(owl_editwin *e) {
894  owl_function_aimlogin(owl_editwin_get_command(e),
895                        owl_editwin_get_text(e));
896}
897
898void owl_function_aimlogin(char *user, char *passwd) {
899  int ret;
900
901  /* clear the buddylist */
902  owl_buddylist_clear(owl_global_get_buddylist(&g));
903
904  /* try to login */
905  ret=owl_aim_login(user, passwd);
906  if (ret) owl_function_makemsg("Warning: login for %s failed.\n", user);
907}
908
909void owl_function_suspend()
910{
911  endwin();
912  printf("\n");
913  kill(getpid(), SIGSTOP);
914
915  /* resize to reinitialize all the windows when we come back */
916  owl_command_resize();
917}
918
919void owl_function_zaway_toggle()
920{
921  if (!owl_global_is_zaway(&g)) {
922    owl_global_set_zaway_msg(&g, owl_global_get_zaway_msg_default(&g));
923    owl_function_zaway_on();
924  } else {
925    owl_function_zaway_off();
926  }
927}
928
929void owl_function_zaway_on()
930{
931  owl_global_set_zaway_on(&g);
932  owl_function_makemsg("zaway set (%s)", owl_global_get_zaway_msg(&g));
933}
934
935void owl_function_zaway_off()
936{
937  owl_global_set_zaway_off(&g);
938  owl_function_makemsg("zaway off");
939}
940
941void owl_function_aaway_toggle()
942{
943  if (!owl_global_is_aaway(&g)) {
944    owl_global_set_aaway_msg(&g, owl_global_get_aaway_msg_default(&g));
945    owl_function_aaway_on();
946  } else {
947    owl_function_aaway_off();
948  }
949}
950
951void owl_function_aaway_on()
952{
953  owl_global_set_aaway_on(&g);
954  /* owl_aim_set_awaymsg(owl_global_get_zaway_msg(&g)); */
955  owl_function_makemsg("AIM away set (%s)", owl_global_get_aaway_msg(&g));
956}
957
958void owl_function_aaway_off()
959{
960  owl_global_set_aaway_off(&g);
961  /* owl_aim_set_awaymsg(""); */
962  owl_function_makemsg("AIM away off");
963}
964
965void owl_function_quit()
966{
967  char *ret;
968 
969  /* zlog out if we need to */
970  if (owl_global_is_shutdownlogout(&g)) {
971    owl_zephyr_zlog_out();
972  }
973
974  /* execute the commands in shutdown */
975  ret = owl_perlconfig_execute("BarnOwl::Hooks::_shutdown();");
976  if (ret) owl_free(ret);
977
978  /* signal our child process, if any */
979  if (owl_global_get_newmsgproc_pid(&g)) {
980    kill(owl_global_get_newmsgproc_pid(&g), SIGHUP);
981  }
982
983  /* Quit zephyr */
984  owl_zephyr_shutdown();
985 
986  /* Quit AIM */
987  if (owl_global_is_aimloggedin(&g)) {
988    owl_aim_logout();
989  }
990
991  /* done with curses */
992  endwin();
993
994  /* restore terminal settings */
995  tcsetattr(0, TCSAFLUSH, owl_global_get_startup_tio(&g));
996
997  owl_function_debugmsg("Quitting Owl");
998  exit(0);
999}
1000
1001void owl_function_openurl()
1002{
1003  /* visit the first url in the current message */
1004  owl_message *m;
1005  owl_view *v;
1006  char *ptr1, *ptr2, *text, url[LINE], tmpbuff[LINE];
1007  int webbrowser;
1008
1009  webbrowser = owl_global_get_webbrowser(&g);
1010
1011  if (webbrowser < 0 || webbrowser == OWL_WEBBROWSER_NONE) {
1012    owl_function_error("No browser selected");
1013    return;
1014  }
1015
1016  v=owl_global_get_current_view(&g);
1017 
1018  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1019
1020  if (!m || owl_view_get_size(v)==0) {
1021    owl_function_error("No current message selected");
1022    return;
1023  }
1024
1025  text=owl_message_get_text(m);
1026
1027  /* First look for a good URL */ 
1028  if ((ptr1=strstr(text, "http://"))!=NULL) {
1029    ptr2=strpbrk(ptr1, " \n\t");
1030    if (ptr2) {
1031      strncpy(url, ptr1, ptr2-ptr1+1);
1032      url[ptr2-ptr1+1]='\0';
1033    } else {
1034      strcpy(url, ptr1);
1035    }
1036
1037    /* if we had <http strip a trailing > */
1038    if (ptr1>text && ptr1[-1]=='<') {
1039      if (url[strlen(url)-1]=='>') {
1040        url[strlen(url)-1]='\0';
1041      }
1042    }
1043  } else if ((ptr1=strstr(text, "https://"))!=NULL) {
1044    /* Look for an https URL */ 
1045    ptr2=strpbrk(ptr1, " \n\t");
1046    if (ptr2) {
1047      strncpy(url, ptr1, ptr2-ptr1+1);
1048      url[ptr2-ptr1+1]='\0';
1049    } else {
1050      strcpy(url, ptr1);
1051    }
1052   
1053    /* if we had <http strip a trailing > */
1054    if (ptr1>text && ptr1[-1]=='<') {
1055      if (url[strlen(url)-1]=='>') {
1056        url[strlen(url)-1]='\0';
1057      }
1058    }
1059  } else if ((ptr1=strstr(text, "www."))!=NULL) {
1060    /* if we can't find a real url look for www.something */
1061    ptr2=strpbrk(ptr1, " \n\t");
1062    if (ptr2) {
1063      strncpy(url, ptr1, ptr2-ptr1+1);
1064      url[ptr2-ptr1+1]='\0';
1065    } else {
1066      strcpy(url, ptr1);
1067    }
1068  } else {
1069    owl_function_beep();
1070    owl_function_error("Could not find URL to open.");
1071    return;
1072  }
1073
1074  /* Make sure there aren't any quotes or \'s in the url */
1075  for (ptr1 = url; *ptr1; ptr1++) {
1076    if (*ptr1 == '"' || *ptr1 == '\\') {
1077      owl_function_beep();
1078      owl_function_error("URL contains invalid characters.");
1079      return;
1080    }
1081  }
1082 
1083  /* NOTE: There are potentially serious security issues here... */
1084
1085  /* open the page */
1086  owl_function_makemsg("Opening %s", url);
1087  if (webbrowser == OWL_WEBBROWSER_NETSCAPE) {
1088    snprintf(tmpbuff, LINE, "netscape -remote \"openURL(%s)\" > /dev/null 2> /dev/null", url);
1089    system(tmpbuff); 
1090  } else if (webbrowser == OWL_WEBBROWSER_GALEON) {
1091    snprintf(tmpbuff, LINE, "galeon \"%s\" > /dev/null 2> /dev/null &", url);
1092    system(tmpbuff); 
1093  } else if (webbrowser == OWL_WEBBROWSER_OPERA) {
1094    snprintf(tmpbuff, LINE, "opera \"%s\" > /dev/null 2> /dev/null &", url);
1095    system(tmpbuff); 
1096  }
1097}
1098
1099void owl_function_calculate_topmsg(int direction)
1100{
1101  int recwinlines, topmsg, curmsg;
1102  owl_view *v;
1103
1104  v=owl_global_get_current_view(&g);
1105  curmsg=owl_global_get_curmsg(&g);
1106  topmsg=owl_global_get_topmsg(&g);
1107  recwinlines=owl_global_get_recwin_lines(&g);
1108
1109  /*
1110  if (owl_view_get_size(v) < 1) {
1111    return;
1112  }
1113  */
1114
1115  switch (owl_global_get_scrollmode(&g)) {
1116  case OWL_SCROLLMODE_TOP:
1117    topmsg = owl_function_calculate_topmsg_top(direction, v, curmsg, topmsg, recwinlines);
1118    break;
1119  case OWL_SCROLLMODE_NEARTOP:
1120    topmsg = owl_function_calculate_topmsg_neartop(direction, v, curmsg, topmsg, recwinlines);
1121    break;
1122  case OWL_SCROLLMODE_CENTER:
1123    topmsg = owl_function_calculate_topmsg_center(direction, v, curmsg, topmsg, recwinlines);
1124    break;
1125  case OWL_SCROLLMODE_PAGED:
1126    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 0);
1127    break;
1128  case OWL_SCROLLMODE_PAGEDCENTER:
1129    topmsg = owl_function_calculate_topmsg_paged(direction, v, curmsg, topmsg, recwinlines, 1);
1130    break;
1131  case OWL_SCROLLMODE_NORMAL:
1132  default:
1133    topmsg = owl_function_calculate_topmsg_normal(direction, v, curmsg, topmsg, recwinlines);
1134  }
1135  owl_function_debugmsg("Calculated a topmsg of %i", topmsg);
1136  owl_global_set_topmsg(&g, topmsg);
1137}
1138
1139/* Returns what the new topmsg should be. 
1140 * Passed the last direction of movement,
1141 * the current view,
1142 * the current message number in the view,
1143 * the top message currently being displayed,
1144 * and the number of lines in the recwin.
1145 */
1146int owl_function_calculate_topmsg_top(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1147{
1148  return(curmsg);
1149}
1150
1151int owl_function_calculate_topmsg_neartop(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1152{
1153  if (curmsg>0 
1154      && (owl_message_get_numlines(owl_view_get_element(v, curmsg-1))
1155          <  recwinlines/2)) {
1156    return(curmsg-1);
1157  } else {
1158    return(curmsg);
1159  }
1160}
1161 
1162int owl_function_calculate_topmsg_center(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1163{
1164  int i, last, lines;
1165
1166  last = curmsg;
1167  lines = 0;
1168  for (i=curmsg-1; i>=0; i--) {
1169    lines += owl_message_get_numlines(owl_view_get_element(v, i));
1170    if (lines > recwinlines/2) break;
1171    last = i;
1172  }
1173  return(last);
1174}
1175 
1176int owl_function_calculate_topmsg_paged(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines, int center_on_page)
1177{
1178  int i, last, lines, savey;
1179 
1180  /* If we're off the top of the screen, scroll up such that the
1181   * curmsg is near the botton of the screen. */
1182  if (curmsg < topmsg) {
1183    last = curmsg;
1184    lines = 0;
1185    for (i=curmsg; i>=0; i--) {
1186      lines += owl_message_get_numlines(owl_view_get_element(v, i));
1187      if (lines > recwinlines) break;
1188    last = i;
1189    }
1190    if (center_on_page) {
1191      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1192    } else {
1193      return(last);
1194    }
1195  }
1196
1197  /* Find number of lines from top to bottom of curmsg (store in savey) */
1198  savey=0;
1199  for (i=topmsg; i<=curmsg; i++) {
1200    savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1201  }
1202
1203  /* if we're off the bottom of the screen, scroll down */
1204  if (savey > recwinlines) {
1205    if (center_on_page) {
1206      return(owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines));
1207    } else {
1208      return(curmsg);
1209    }
1210  }
1211
1212  /* else just stay as we are... */
1213  return(topmsg);
1214}
1215
1216int owl_function_calculate_topmsg_normal(int direction, owl_view *v, int curmsg, int topmsg, int recwinlines)
1217{
1218  int savey, i, foo, y;
1219
1220  if (curmsg<0) return(topmsg);
1221   
1222  /* If we're off the top of the screen then center */
1223  if (curmsg<topmsg) {
1224    topmsg=owl_function_calculate_topmsg_center(direction, v, curmsg, 0, recwinlines);
1225  }
1226
1227  /* If curmsg is so far past topmsg that there are more messages than
1228     lines, skip the line counting that follows because we're
1229     certainly off screen.  */
1230  savey=curmsg-topmsg;
1231  if (savey <= recwinlines) {
1232    /* Find number of lines from top to bottom of curmsg (store in savey) */
1233    savey = 0;
1234    for (i=topmsg; i<=curmsg; i++) {
1235      savey+=owl_message_get_numlines(owl_view_get_element(v, i));
1236    }
1237  }
1238
1239  /* If we're off the bottom of the screen, set the topmsg to curmsg
1240   * and scroll upwards */
1241  if (savey > recwinlines) {
1242    topmsg=curmsg;
1243    savey=owl_message_get_numlines(owl_view_get_element(v, curmsg));
1244    direction=OWL_DIRECTION_UPWARDS;
1245  }
1246 
1247  /* If our bottom line is less than 1/4 down the screen then scroll up */
1248  if (direction == OWL_DIRECTION_UPWARDS || direction == OWL_DIRECTION_NONE) {
1249    if (savey < (recwinlines / 4)) {
1250      y=0;
1251      for (i=curmsg; i>=0; i--) {
1252        foo=owl_message_get_numlines(owl_view_get_element(v, i));
1253        /* will we run the curmsg off the screen? */
1254        if ((foo+y) >= recwinlines) {
1255          i++;
1256          if (i>curmsg) i=curmsg;
1257          break;
1258        }
1259        /* have saved 1/2 the screen space? */
1260        y+=foo;
1261        if (y > (recwinlines / 2)) break;
1262      }
1263      if (i<0) i=0;
1264      return(i);
1265    }
1266  }
1267
1268  if (direction == OWL_DIRECTION_DOWNWARDS || direction == OWL_DIRECTION_NONE) {
1269    /* If curmsg bottom line is more than 3/4 down the screen then scroll down */
1270    if (savey > ((recwinlines * 3)/4)) {
1271      y=0;
1272      /* count lines from the top until we can save 1/2 the screen size */
1273      for (i=topmsg; i<curmsg; i++) {
1274        y+=owl_message_get_numlines(owl_view_get_element(v, i));
1275        if (y > (recwinlines / 2)) break;
1276      }
1277      if (i==curmsg) {
1278        i--;
1279      }
1280      return(i+1);
1281    }
1282  }
1283
1284  return(topmsg);
1285}
1286
1287void owl_function_resize()
1288{
1289  owl_global_set_resize_pending(&g);
1290}
1291
1292void owl_function_run_buffercommand()
1293{
1294  owl_editwin_do_callback(owl_global_get_typwin(&g));
1295}
1296
1297void owl_function_debugmsg(char *fmt, ...)
1298{
1299  FILE *file;
1300  time_t now;
1301  char buff1[LINE], buff2[LINE];
1302  va_list ap;
1303  va_start(ap, fmt);
1304
1305  if (!owl_global_is_debug_fast(&g)) return;
1306
1307  file=fopen(owl_global_get_debug_file(&g), "a");
1308  if (!file) return;
1309
1310  now=time(NULL);
1311  strcpy(buff1, ctime(&now));
1312  buff1[strlen(buff1)-1]='\0';
1313
1314  owl_global_get_runtime_string(&g, buff2);
1315 
1316  fprintf(file, "[%i -  %s - %s]: ", (int) getpid(), buff1, buff2);
1317  vfprintf(file, fmt, ap);
1318  fprintf(file, "\n");
1319  fclose(file);
1320
1321  va_end(ap);
1322}
1323
1324void owl_function_refresh()
1325{
1326  owl_function_resize();
1327}
1328
1329void owl_function_beep()
1330{
1331  if (owl_global_is_bell(&g)) {
1332    beep();
1333    owl_global_set_needrefresh(&g); /* do we really need this? */
1334  }
1335}
1336
1337void owl_function_subscribe(char *class, char *inst, char *recip)
1338{
1339  int ret;
1340
1341  ret=owl_zephyr_sub(class, inst, recip);
1342  if (ret) {
1343    owl_function_error("Error subscribing.");
1344  } else {
1345    owl_function_makemsg("Subscribed.");
1346  }
1347}
1348
1349void owl_function_unsubscribe(char *class, char *inst, char *recip)
1350{
1351  int ret;
1352
1353  ret=owl_zephyr_unsub(class, inst, recip);
1354  if (ret) {
1355    owl_function_error("Error subscribing.");
1356  } else {
1357    owl_function_makemsg("Unsubscribed.");
1358  }
1359}
1360
1361void owl_function_set_cursor(WINDOW *win)
1362{
1363  wnoutrefresh(win);
1364}
1365
1366void owl_function_full_redisplay()
1367{
1368  redrawwin(owl_global_get_curs_recwin(&g));
1369  redrawwin(owl_global_get_curs_sepwin(&g));
1370  // Work around curses segfualts with windows off the screen
1371  if (g.lines >= owl_global_get_typwin_lines(&g)+2)
1372      redrawwin(owl_global_get_curs_typwin(&g));
1373  if (g.lines >= 2)
1374      redrawwin(owl_global_get_curs_msgwin(&g));
1375
1376  wnoutrefresh(owl_global_get_curs_recwin(&g));
1377  wnoutrefresh(owl_global_get_curs_sepwin(&g));
1378  wnoutrefresh(owl_global_get_curs_typwin(&g));
1379  wnoutrefresh(owl_global_get_curs_msgwin(&g));
1380
1381  if (owl_popwin_is_active(owl_global_get_popwin(&g))) {
1382    owl_popwin_refresh(owl_global_get_popwin(&g));
1383  }
1384 
1385  sepbar("");
1386  owl_function_makemsg("");
1387
1388  owl_global_set_needrefresh(&g);
1389}
1390
1391void owl_function_popless_text(char *text)
1392{
1393  owl_popwin *pw;
1394  owl_viewwin *v;
1395
1396  pw=owl_global_get_popwin(&g);
1397  v=owl_global_get_viewwin(&g);
1398
1399  owl_popwin_up(pw);
1400  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
1401                        owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
1402                        text);
1403  owl_popwin_refresh(pw);
1404  owl_viewwin_redisplay(v, 0);
1405  owl_global_set_needrefresh(&g);
1406}
1407
1408void owl_function_popless_fmtext(owl_fmtext *fm)
1409{
1410  owl_popwin *pw;
1411  owl_viewwin *v;
1412
1413  pw=owl_global_get_popwin(&g);
1414  v=owl_global_get_viewwin(&g);
1415
1416  owl_popwin_up(pw);
1417  owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw),
1418                   owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
1419                   fm);
1420  owl_popwin_refresh(pw);
1421  owl_viewwin_redisplay(v, 0);
1422  owl_global_set_needrefresh(&g);
1423}
1424
1425void owl_function_popless_file(char *filename)
1426{
1427  owl_fmtext fm;
1428  FILE *file;
1429  char buff[1024];
1430
1431  file=fopen(filename, "r");
1432  if (!file) {
1433    owl_function_error("Could not open file: %s", filename);
1434    return;
1435  }
1436
1437  owl_fmtext_init_null(&fm);
1438  while (fgets(buff, 1024, file)) {
1439    owl_fmtext_append_normal(&fm, buff);
1440    /*    owl_fmtext_append_normal(&fm, "\n"); */
1441  }
1442
1443  owl_function_popless_fmtext(&fm);
1444  owl_fmtext_free(&fm);
1445  fclose(file);
1446}
1447
1448void owl_function_about()
1449{
1450  char buff[5000];
1451
1452  sprintf(buff, "This is barnowl version %s\n\n", OWL_VERSION_STRING);
1453  strcat(buff, "barnowl is a fork of the Owl zephyr client, written and\n");
1454  strcat(buff, "maintained by Alejandro Sedeno and Nelson Elhage at the\n");
1455  strcat(buff, "Massachusetts Institute of Technology. \n");
1456  strcat(buff, "\n");
1457  strcat(buff, "Owl was written by James Kretchmar. The first version, 0.5, was\n");
1458  strcat(buff, "released in March 2002.\n");
1459  strcat(buff, "\n");
1460  strcat(buff, "The name 'owl' was chosen in reference to the owls in the\n");
1461  strcat(buff, "Harry Potter novels, who are tasked with carrying messages\n");
1462  strcat(buff, "between Witches and Wizards. The name 'barnowl' was chosen\n");
1463  strcat(buff, "because we feel our owls should live closer to our ponies.\n");
1464  strcat(buff, "\n");
1465  strcat(buff, "Copyright 2002 Massachusetts Institute of Technology\n");
1466  strcat(buff, "\n");
1467  strcat(buff, "Permission to use, copy, modify, and distribute this\n");
1468  strcat(buff, "software and its documentation for any purpose and without\n");
1469  strcat(buff, "fee is hereby granted, provided that the above copyright\n");
1470  strcat(buff, "notice and this permission notice appear in all copies\n");
1471  strcat(buff, "and in supporting documentation.  No representation is\n");
1472  strcat(buff, "made about the suitability of this software for any\n");
1473  strcat(buff, "purpose.  It is provided \"as is\" without express\n");
1474  strcat(buff, "or implied warranty.\n");
1475  owl_function_popless_text(buff);
1476}
1477
1478void owl_function_info()
1479{
1480  owl_message *m;
1481  owl_fmtext fm, attrfm;
1482  char buff[10000];
1483  owl_view *v;
1484#ifdef HAVE_LIBZEPHYR
1485  ZNotice_t *n;
1486#endif
1487
1488  owl_fmtext_init_null(&fm);
1489 
1490  v=owl_global_get_current_view(&g);
1491  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1492  if (!m || owl_view_get_size(v)==0) {
1493    owl_function_error("No message selected\n");
1494    return;
1495  }
1496
1497  owl_fmtext_append_bold(&fm, "General Information:\n");
1498  owl_fmtext_append_normal(&fm, "  Msg Id    : ");
1499  sprintf(buff, "%i", owl_message_get_id(m));
1500  owl_fmtext_append_normal(&fm, buff);
1501  owl_fmtext_append_normal(&fm, "\n");
1502
1503  owl_fmtext_append_normal(&fm, "  Type      : ");
1504  owl_fmtext_append_bold(&fm, owl_message_get_type(m));
1505  owl_fmtext_append_normal(&fm, "\n");
1506
1507  if (owl_message_is_direction_in(m)) {
1508    owl_fmtext_append_normal(&fm, "  Direction : in\n");
1509  } else if (owl_message_is_direction_out(m)) {
1510    owl_fmtext_append_normal(&fm, "  Direction : out\n");
1511  } else if (owl_message_is_direction_none(m)) {
1512    owl_fmtext_append_normal(&fm, "  Direction : none\n");
1513  } else {
1514    owl_fmtext_append_normal(&fm, "  Direction : unknown\n");
1515  }
1516
1517  owl_fmtext_append_normal(&fm, "  Time      : ");
1518  owl_fmtext_append_normal(&fm, owl_message_get_timestr(m));
1519  owl_fmtext_append_normal(&fm, "\n");
1520
1521  if (!owl_message_is_type_admin(m)) {
1522    owl_fmtext_append_normal(&fm, "  Sender    : ");
1523    owl_fmtext_append_normal(&fm, owl_message_get_sender(m));
1524    owl_fmtext_append_normal(&fm, "\n");
1525   
1526    owl_fmtext_append_normal(&fm, "  Recipient : ");
1527    owl_fmtext_append_normal(&fm, owl_message_get_recipient(m));
1528    owl_fmtext_append_normal(&fm, "\n");
1529  }
1530   
1531  if (owl_message_is_type_zephyr(m)) {
1532    owl_fmtext_append_bold(&fm, "\nZephyr Specific Information:\n");
1533   
1534    owl_fmtext_append_normal(&fm, "  Class     : ");
1535    owl_fmtext_append_normal(&fm, owl_message_get_class(m));
1536    owl_fmtext_append_normal(&fm, "\n");
1537    owl_fmtext_append_normal(&fm, "  Instance  : ");
1538    owl_fmtext_append_normal(&fm, owl_message_get_instance(m));
1539    owl_fmtext_append_normal(&fm, "\n");
1540    owl_fmtext_append_normal(&fm, "  Opcode    : ");
1541    owl_fmtext_append_normal(&fm, owl_message_get_opcode(m));
1542    owl_fmtext_append_normal(&fm, "\n");
1543   
1544    owl_fmtext_append_normal(&fm, "  Time      : ");
1545    owl_fmtext_append_normal(&fm, owl_message_get_timestr(m));
1546    owl_fmtext_append_normal(&fm, "\n");
1547#ifdef HAVE_LIBZEPHYR
1548    if (owl_message_is_direction_in(m)) {
1549      char *ptr, tmpbuff[1024];
1550      int i, j, fields, len;
1551
1552      n=owl_message_get_notice(m);
1553
1554      if (!owl_message_is_pseudo(m)) {
1555        owl_fmtext_append_normal(&fm, "  Kind      : ");
1556        if (n->z_kind==UNSAFE) {
1557          owl_fmtext_append_normal(&fm, "UNSAFE\n");
1558        } else if (n->z_kind==UNACKED) {
1559          owl_fmtext_append_normal(&fm, "UNACKED\n");
1560        } else if (n->z_kind==ACKED) {
1561          owl_fmtext_append_normal(&fm, "ACKED\n");
1562        } else if (n->z_kind==HMACK) {
1563          owl_fmtext_append_normal(&fm, "HMACK\n");
1564        } else if (n->z_kind==HMCTL) {
1565          owl_fmtext_append_normal(&fm, "HMCTL\n");
1566        } else if (n->z_kind==SERVACK) {
1567          owl_fmtext_append_normal(&fm, "SERVACK\n");
1568        } else if (n->z_kind==SERVNAK) {
1569          owl_fmtext_append_normal(&fm, "SERVNACK\n");
1570        } else if (n->z_kind==CLIENTACK) {
1571          owl_fmtext_append_normal(&fm, "CLIENTACK\n");
1572        } else if (n->z_kind==STAT) {
1573          owl_fmtext_append_normal(&fm, "STAT\n");
1574        } else {
1575          owl_fmtext_append_normal(&fm, "ILLEGAL VALUE\n");
1576        }
1577      }
1578      owl_fmtext_append_normal(&fm, "  Host      : ");
1579      owl_fmtext_append_normal(&fm, owl_message_get_hostname(m));
1580
1581      if (!owl_message_is_pseudo(m)) {
1582        owl_fmtext_append_normal(&fm, "\n");
1583        sprintf(buff, "  Port      : %i\n", ntohs(n->z_port));
1584        owl_fmtext_append_normal(&fm, buff);
1585
1586        owl_fmtext_append_normal(&fm,    "  Auth      : ");
1587        owl_fmtext_append_normal(&fm, owl_zephyr_get_authstr(n));
1588        owl_fmtext_append_normal(&fm, "\n");
1589       
1590        /* fix this */
1591        sprintf(buff, "  Checkd Ath: %i\n", n->z_checked_auth);
1592        sprintf(buff, "%s  Multi notc: %s\n", buff, n->z_multinotice);
1593        sprintf(buff, "%s  Num other : %i\n", buff, n->z_num_other_fields);
1594        sprintf(buff, "%s  Msg Len   : %i\n", buff, n->z_message_len);
1595        owl_fmtext_append_normal(&fm, buff);
1596       
1597        sprintf(buff, "  Fields    : %i\n", owl_zephyr_get_num_fields(n));
1598        owl_fmtext_append_normal(&fm, buff);
1599       
1600        fields=owl_zephyr_get_num_fields(n);
1601        for (i=0; i<fields; i++) {
1602          sprintf(buff, "  Field %i   : ", i+1);
1603         
1604          ptr=owl_zephyr_get_field(n, i+1);
1605          len=strlen(ptr);
1606          if (len<30) {
1607            strncpy(tmpbuff, ptr, len);
1608            tmpbuff[len]='\0';
1609          } else {
1610            strncpy(tmpbuff, ptr, 30);
1611            tmpbuff[30]='\0';
1612            strcat(tmpbuff, "...");
1613          }
1614          owl_free(ptr);
1615         
1616          for (j=0; j<strlen(tmpbuff); j++) {
1617            if (tmpbuff[j]=='\n') tmpbuff[j]='~';
1618            if (tmpbuff[j]=='\r') tmpbuff[j]='!';
1619          }
1620         
1621          strcat(buff, tmpbuff);
1622          strcat(buff, "\n");
1623          owl_fmtext_append_normal(&fm, buff);
1624        }
1625        owl_fmtext_append_normal(&fm, "  Default Fm:");
1626        owl_fmtext_append_normal(&fm, n->z_default_format);
1627      }
1628     
1629    }
1630#endif   
1631  }
1632
1633  if (owl_message_is_type_aim(m)) {
1634    owl_fmtext_append_bold(&fm, "\nAIM Specific Information:\n");
1635  }
1636
1637  owl_fmtext_append_bold(&fm, "\nOwl Message Attributes:\n");
1638  owl_message_attributes_tofmtext(m, &attrfm);
1639  owl_fmtext_append_fmtext(&fm, &attrfm);
1640 
1641  owl_function_popless_fmtext(&fm);
1642  owl_fmtext_free(&fm);
1643  owl_fmtext_free(&attrfm);
1644}
1645
1646/* print the current message in a popup window.
1647 * Use the 'default' style regardless of whatever
1648 * style the user may be using
1649 */
1650void owl_function_curmsg_to_popwin()
1651{
1652  owl_popwin *pw;
1653  owl_view *v;
1654  owl_message *m;
1655  owl_style *s;
1656  owl_fmtext fm;
1657
1658  v=owl_global_get_current_view(&g);
1659  s=owl_global_get_style_by_name(&g, "default");
1660  pw=owl_global_get_popwin(&g);
1661
1662  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
1663
1664  if (!m || owl_view_get_size(v)==0) {
1665    owl_function_error("No current message");
1666    return;
1667  }
1668
1669  owl_fmtext_init_null(&fm);
1670  owl_style_get_formattext(s, &fm, m);
1671
1672  owl_function_popless_fmtext(&fm);
1673  owl_fmtext_free(&fm);
1674}
1675
1676void owl_function_page_curmsg(int step)
1677{
1678  /* scroll down or up within the current message IF the message is truncated */
1679
1680  int offset, curmsg, lines;
1681  owl_view *v;
1682  owl_message *m;
1683
1684  offset=owl_global_get_curmsg_vert_offset(&g);
1685  v=owl_global_get_current_view(&g);
1686  curmsg=owl_global_get_curmsg(&g);
1687  m=owl_view_get_element(v, curmsg);
1688  if (!m || owl_view_get_size(v)==0) return;
1689  lines=owl_message_get_numlines(m);
1690
1691  if (offset==0) {
1692    /* Bail if the curmsg isn't the last one displayed */
1693    if (curmsg != owl_mainwin_get_last_msg(owl_global_get_mainwin(&g))) {
1694      owl_function_makemsg("The entire message is already displayed");
1695      return;
1696    }
1697   
1698    /* Bail if we're not truncated */
1699    if (!owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
1700      owl_function_makemsg("The entire message is already displayed");
1701      return;
1702    }
1703  }
1704 
1705 
1706  /* don't scroll past the last line */
1707  if (step>0) {
1708    if (offset+step > lines-1) {
1709      owl_global_set_curmsg_vert_offset(&g, lines-1);
1710    } else {
1711      owl_global_set_curmsg_vert_offset(&g, offset+step);
1712    }
1713  }
1714
1715  /* would we be before the beginning of the message? */
1716  if (step<0) {
1717    if (offset+step<0) {
1718      owl_global_set_curmsg_vert_offset(&g, 0);
1719    } else {
1720      owl_global_set_curmsg_vert_offset(&g, offset+step);
1721    }
1722  }
1723 
1724  /* redisplay */
1725  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1726  owl_global_set_needrefresh(&g);
1727}
1728
1729void owl_function_resize_typwin(int newsize)
1730{
1731  owl_global_set_typwin_lines(&g, newsize);
1732  owl_function_resize();
1733}
1734
1735void owl_function_typwin_grow()
1736{
1737  int i;
1738
1739  i=owl_global_get_typwin_lines(&g);
1740  owl_function_resize_typwin(i+1);
1741}
1742
1743void owl_function_typwin_shrink()
1744{
1745  int i;
1746
1747  i=owl_global_get_typwin_lines(&g);
1748  if (i>2) {
1749    owl_function_resize_typwin(i-1);
1750  }
1751}
1752
1753void owl_function_mainwin_pagedown()
1754{
1755  int i;
1756
1757  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
1758  if (i<0) return;
1759  if (owl_mainwin_is_last_msg_truncated(owl_global_get_mainwin(&g))
1760      && (owl_global_get_curmsg(&g) < i)
1761      && (i>0)) {
1762    i--;
1763  }
1764  owl_global_set_curmsg(&g, i);
1765  owl_function_nextmsg();
1766}
1767
1768void owl_function_mainwin_pageup()
1769{
1770  owl_global_set_curmsg(&g, owl_global_get_topmsg(&g));
1771  owl_function_prevmsg();
1772}
1773
1774void owl_function_getsubs()
1775{
1776  char *buff;
1777
1778  buff=owl_zephyr_getsubs();
1779
1780  if (buff) {
1781    owl_function_popless_text(buff);
1782  } else {
1783    owl_function_popless_text("Error getting subscriptions");
1784  }
1785           
1786  owl_free(buff);
1787}
1788
1789#define PABUFLEN 5000
1790void owl_function_printallvars()
1791{
1792  char buff[PABUFLEN], *pos, *name;
1793  owl_list varnames;
1794  int i, numvarnames, rem;
1795
1796  pos = buff;
1797  pos += sprintf(pos, "%-20s = %s\n", "VARIABLE", "VALUE");
1798  pos += sprintf(pos, "%-20s   %s\n",  "--------", "-----");
1799  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1800  rem = (buff+PABUFLEN)-pos-1;
1801  numvarnames = owl_list_get_size(&varnames);
1802  for (i=0; i<numvarnames; i++) {
1803    name = owl_list_get_element(&varnames, i);
1804    if (name && name[0]!='_') {
1805      rem = (buff+PABUFLEN)-pos-1;   
1806      pos += snprintf(pos, rem, "\n%-20s = ", name);
1807      rem = (buff+PABUFLEN)-pos-1;   
1808      owl_variable_get_tostring(owl_global_get_vardict(&g), name, pos, rem);
1809      pos = buff+strlen(buff);
1810    }
1811  }
1812  rem = (buff+PABUFLEN)-pos-1;   
1813  snprintf(pos, rem, "\n");
1814  owl_variable_dict_namelist_free(&varnames);
1815 
1816  owl_function_popless_text(buff);
1817}
1818
1819void owl_function_show_variables()
1820{
1821  owl_list varnames;
1822  owl_fmtext fm; 
1823  int i, numvarnames;
1824  char *varname;
1825
1826  owl_fmtext_init_null(&fm);
1827  owl_fmtext_append_bold(&fm, 
1828      "Variables: (use 'show variable <name>' for details)\n");
1829  owl_variable_dict_get_names(owl_global_get_vardict(&g), &varnames);
1830  numvarnames = owl_list_get_size(&varnames);
1831  for (i=0; i<numvarnames; i++) {
1832    varname = owl_list_get_element(&varnames, i);
1833    if (varname && varname[0]!='_') {
1834      owl_variable_describe(owl_global_get_vardict(&g), varname, &fm);
1835    }
1836  }
1837  owl_variable_dict_namelist_free(&varnames);
1838  owl_function_popless_fmtext(&fm);
1839  owl_fmtext_free(&fm);
1840}
1841
1842void owl_function_show_variable(char *name)
1843{
1844  owl_fmtext fm; 
1845
1846  owl_fmtext_init_null(&fm);
1847  owl_variable_get_help(owl_global_get_vardict(&g), name, &fm);
1848  owl_function_popless_fmtext(&fm);
1849  owl_fmtext_free(&fm); 
1850}
1851
1852/* note: this applies to global message list, not to view.
1853 * If flag is 1, deletes.  If flag is 0, undeletes. */
1854void owl_function_delete_by_id(int id, int flag)
1855{
1856  owl_messagelist *ml;
1857  owl_message *m;
1858  ml = owl_global_get_msglist(&g);
1859  m = owl_messagelist_get_by_id(ml, id);
1860  if (m) {
1861    if (flag == 1) {
1862      owl_message_mark_delete(m);
1863    } else if (flag == 0) {
1864      owl_message_unmark_delete(m);
1865    }
1866    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1867    owl_global_set_needrefresh(&g);
1868  } else {
1869    owl_function_error("No message with id %d: unable to mark for (un)delete",id);
1870  }
1871}
1872
1873void owl_function_delete_automsgs()
1874{
1875  /* mark for deletion all messages in the current view that match the
1876   * 'trash' filter */
1877
1878  int i, j, count;
1879  owl_message *m;
1880  owl_view *v;
1881  owl_filter *f;
1882
1883  /* get the trash filter */
1884  f=owl_global_get_filter(&g, "trash");
1885  if (!f) {
1886    owl_function_error("No trash filter defined");
1887    return;
1888  }
1889
1890  v=owl_global_get_current_view(&g);
1891
1892  count=0;
1893  j=owl_view_get_size(v);
1894  for (i=0; i<j; i++) {
1895    m=owl_view_get_element(v, i);
1896    if (owl_filter_message_match(f, m)) {
1897      count++;
1898      owl_message_mark_delete(m);
1899    }
1900  }
1901  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
1902  owl_function_makemsg("%i messages marked for deletion", count);
1903  owl_global_set_needrefresh(&g);
1904}
1905
1906void owl_function_status()
1907{
1908  char buff[5000];
1909  time_t start;
1910  int up, days, hours, minutes;
1911  owl_fmtext fm;
1912
1913  owl_fmtext_init_null(&fm);
1914
1915  start=owl_global_get_starttime(&g);
1916
1917  owl_fmtext_append_normal(&fm, "General Information:\n");
1918
1919  owl_fmtext_append_normal(&fm, "  Version: ");
1920  owl_fmtext_append_normal(&fm, OWL_VERSION_STRING);
1921  owl_fmtext_append_normal(&fm, "\n");
1922
1923  owl_fmtext_append_normal(&fm, "  Startup Arugments: ");
1924  owl_fmtext_append_normal(&fm, owl_global_get_startupargs(&g));
1925  owl_fmtext_append_normal(&fm, "\n");
1926
1927  owl_fmtext_append_normal(&fm, "  Current Directory: ");
1928  (void) getcwd(buff, MAXPATHLEN);
1929  owl_fmtext_append_normal(&fm, buff);
1930  owl_fmtext_append_normal(&fm, "\n");
1931
1932  sprintf(buff, "  Startup Time: %s", ctime(&start));
1933  owl_fmtext_append_normal(&fm, buff);
1934
1935  up=owl_global_get_runtime(&g);
1936  days=up/86400;
1937  up-=days*86400;
1938  hours=up/3600;
1939  up-=hours*3600;
1940  minutes=up/60;
1941  up-=minutes*60;
1942  sprintf(buff, "  Run Time: %i days %2.2i:%2.2i:%2.2i\n", days, hours, minutes, up);
1943  owl_fmtext_append_normal(&fm, buff);
1944
1945  owl_fmtext_append_normal(&fm, "\nProtocol Options:\n");
1946  owl_fmtext_append_normal(&fm, "  Zephyr included    : ");
1947  if (owl_global_is_havezephyr(&g)) {
1948    owl_fmtext_append_normal(&fm, "yes\n");
1949  } else {
1950    owl_fmtext_append_normal(&fm, "no\n");
1951  }
1952  owl_fmtext_append_normal(&fm, "  AIM included       : yes\n");
1953  owl_fmtext_append_normal(&fm, "  Loopback included  : yes\n");
1954
1955
1956  owl_fmtext_append_normal(&fm, "\nBuild Options:\n");
1957  owl_fmtext_append_normal(&fm, "  Stderr redirection : ");
1958#if OWL_STDERR_REDIR
1959  owl_fmtext_append_normal(&fm, "yes\n");
1960#else
1961  owl_fmtext_append_normal(&fm, "no\n");
1962#endif
1963 
1964
1965  owl_fmtext_append_normal(&fm, "\nMemory Usage:\n");
1966  owl_fmtext_append_normal(&fm, "  Not currently available.\n");
1967  /*
1968  sprintf(buff, "%sMemory Malloced: %i\n", buff, owl_global_get_malloced(&g));
1969  sprintf(buff, "%sMemory Freed: %i\n", buff, owl_global_get_freed(&g));
1970  sprintf(buff, "%sMemory In Use: %i\n", buff, owl_global_get_meminuse(&g));
1971  */
1972
1973  owl_fmtext_append_normal(&fm, "\nAIM Status:\n");
1974  owl_fmtext_append_normal(&fm, "  Logged in: ");
1975  if (owl_global_is_aimloggedin(&g)) {
1976    owl_fmtext_append_normal(&fm, owl_global_get_aim_screenname(&g));
1977    owl_fmtext_append_normal(&fm, "\n");
1978  } else {
1979    owl_fmtext_append_normal(&fm, "(not logged in)\n");
1980  }
1981
1982  owl_fmtext_append_normal(&fm, "  Processing events: ");
1983  if (owl_global_is_doaimevents(&g)) {
1984    owl_fmtext_append_normal(&fm, "yes\n");
1985  } else {
1986    owl_fmtext_append_normal(&fm, "no\n");
1987  }
1988
1989  owl_function_popless_fmtext(&fm);
1990  owl_fmtext_free(&fm);
1991}
1992
1993void owl_function_show_term()
1994{
1995  owl_fmtext fm;
1996  char buff[LINE];
1997
1998  owl_fmtext_init_null(&fm);
1999  sprintf(buff, "Terminal Lines: %i\nTerminal Columns: %i\n",
2000          owl_global_get_lines(&g),
2001          owl_global_get_cols(&g));
2002  owl_fmtext_append_normal(&fm, buff);
2003
2004  if (owl_global_get_hascolors(&g)) {
2005    owl_fmtext_append_normal(&fm, "Color: Yes\n");
2006    sprintf(buff, "Number of color pairs: %i\n", owl_global_get_colorpairs(&g));
2007    owl_fmtext_append_normal(&fm, buff);
2008    sprintf(buff, "Can change colors: %s\n", can_change_color() ? "yes" : "no");
2009    owl_fmtext_append_normal(&fm, buff);
2010  } else {
2011    owl_fmtext_append_normal(&fm, "Color: No\n");
2012  }
2013
2014  owl_function_popless_fmtext(&fm);
2015  owl_fmtext_free(&fm);
2016}
2017
2018/* if type = 0 then normal reply.
2019 * if type = 1 then it's a reply to sender
2020 * if enter = 0 then allow the command to be edited
2021 * if enter = 1 then don't wait for editing
2022 */
2023void owl_function_reply(int type, int enter)
2024{
2025  char *buff=NULL, *oldbuff;
2026  owl_message *m;
2027  owl_filter *f;
2028 
2029  if (owl_view_get_size(owl_global_get_current_view(&g))==0) {
2030    owl_function_error("No message selected");
2031  } else {
2032    char *class, *inst, *to, *cc=NULL;
2033   
2034    m=owl_view_get_element(owl_global_get_current_view(&g), owl_global_get_curmsg(&g));
2035    if (!m) {
2036      owl_function_error("No message selected");
2037      return;
2038    }
2039
2040    /* first check if we catch the reply-lockout filter */
2041    f=owl_global_get_filter(&g, "reply-lockout");
2042    if (f) {
2043      if (owl_filter_message_match(f, m)) {
2044        owl_function_error("Sorry, replies to this message have been disabled by the reply-lockout filter");
2045        return;
2046      }
2047    }
2048
2049    /* loopback */
2050    if (owl_message_is_type_loopback(m)) {
2051      owl_function_loopwrite_setup();
2052      return;
2053    }
2054
2055    /* zephyr */
2056    if (owl_message_is_type_zephyr(m)) {
2057      /* if it's a zephyr we sent, send it out the same way again */
2058      if (owl_message_is_direction_out(m)) {
2059        owl_function_zwrite_setup(owl_message_get_zwriteline(m));
2060        owl_global_set_buffercommand(&g, owl_message_get_zwriteline(m));
2061        return;
2062      }
2063
2064      /* Special case a personal reply to a webzephyr user on a class */
2065      if ((type==1) && !strcasecmp(owl_message_get_opcode(m), OWL_WEBZEPHYR_OPCODE)) {
2066        class=OWL_WEBZEPHYR_CLASS;
2067        inst=owl_message_get_sender(m);
2068        to=OWL_WEBZEPHYR_PRINCIPAL;
2069      } else if (!strcasecmp(owl_message_get_class(m), OWL_WEBZEPHYR_CLASS) && owl_message_is_loginout(m)) {
2070        /* Special case LOGIN/LOGOUT notifications on class "webzephyr" */
2071        class=OWL_WEBZEPHYR_CLASS;
2072        inst=owl_message_get_instance(m);
2073        to=OWL_WEBZEPHYR_PRINCIPAL;
2074      } else if (owl_message_is_loginout(m)) {
2075        /* Normal LOGIN/LOGOUT messages */
2076        class="MESSAGE";
2077        inst="PERSONAL";
2078        to=owl_message_get_sender(m);
2079      } else if (type==1) {
2080        /* Personal reply */
2081        class="MESSAGE";
2082        inst="PERSONAL";
2083        to=owl_message_get_sender(m);
2084      } else {
2085        /* General reply */
2086        class=owl_message_get_class(m);
2087        inst=owl_message_get_instance(m);
2088        to=owl_message_get_recipient(m);
2089        cc=owl_message_get_cc(m);
2090        if (!strcmp(to, "") || !strcmp(to, "*")) {
2091          to="";
2092        } else if (to[0]=='@') {
2093          /* leave it, to get the realm */
2094        } else {
2095          to=owl_message_get_sender(m);
2096        }
2097      }
2098       
2099      /* create the command line */
2100      if (!strcasecmp(owl_message_get_opcode(m), "CRYPT")) {
2101        buff=owl_strdup("zcrypt");
2102      } else {
2103        buff = owl_strdup("zwrite");
2104      }
2105      if (strcasecmp(class, "message")) {
2106        buff = owl_sprintf("%s -c %s%s%s", oldbuff=buff, owl_getquoting(class), class, owl_getquoting(class));
2107        owl_free(oldbuff);
2108      }
2109      if (strcasecmp(inst, "personal")) {
2110        buff = owl_sprintf("%s -i %s%s%s", oldbuff=buff, owl_getquoting(inst), inst, owl_getquoting(inst));
2111        owl_free(oldbuff);
2112      }
2113      if (*to != '\0') {
2114        char *tmp, *oldtmp, *tmp2;
2115        tmp=short_zuser(to);
2116        if (cc) {
2117          tmp = owl_util_uniq(oldtmp=tmp, cc, "-");
2118          owl_free(oldtmp);
2119          buff = owl_sprintf("%s -C %s", oldbuff=buff, tmp);
2120          owl_free(oldbuff);
2121        } else {
2122          if (owl_global_is_smartstrip(&g)) {
2123            tmp2=tmp;
2124            tmp=owl_zephyr_smartstripped_user(tmp2);
2125            owl_free(tmp2);
2126          }
2127          buff = owl_sprintf("%s %s", oldbuff=buff, tmp);
2128          owl_free(oldbuff);
2129        }
2130        owl_free(tmp);
2131      }
2132      if (cc) owl_free(cc);
2133    } else if (owl_message_is_type_aim(m)) {
2134      /* aim */
2135      if (owl_message_is_direction_out(m)) {
2136        buff=owl_sprintf("aimwrite %s", owl_message_get_recipient(m));
2137      } else {
2138        buff=owl_sprintf("aimwrite %s", owl_message_get_sender(m));
2139      }
2140    } else {
2141      char *cmd;
2142      if((type==0 && (cmd=owl_message_get_attribute_value(m, "replycmd")))
2143         || (type==1 && (cmd=owl_message_get_attribute_value(m, "replysendercmd")))) {
2144        buff = owl_strdup(cmd);
2145      }
2146    }
2147
2148    if(!buff) {
2149        owl_function_error("I don't know how to reply to that message.");
2150        return;
2151    }
2152   
2153    if (enter) {
2154      owl_history *hist = owl_global_get_cmd_history(&g);
2155      owl_history_store(hist, buff);
2156      owl_history_reset(hist);
2157      owl_function_command_norv(buff);
2158    } else {
2159      owl_function_start_command(buff);
2160    }
2161    owl_free(buff);
2162  }
2163}
2164
2165void owl_function_zlocate(int argc, char **argv, int auth)
2166{
2167  owl_fmtext fm;
2168  char *ptr, buff[LINE];
2169  int i;
2170
2171  owl_fmtext_init_null(&fm);
2172
2173  for (i=0; i<argc; i++) {
2174    ptr=long_zuser(argv[i]);
2175    owl_zephyr_zlocate(ptr, buff, auth);
2176    owl_fmtext_append_normal(&fm, buff);
2177    owl_free(ptr);
2178  }
2179
2180  owl_function_popless_fmtext(&fm);
2181  owl_fmtext_free(&fm);
2182}
2183
2184void owl_function_start_command(char *line)
2185{
2186  int i, j;
2187  owl_editwin *tw;
2188
2189  tw=owl_global_get_typwin(&g);
2190  owl_global_set_typwin_active(&g);
2191  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, 
2192                        owl_global_get_cmd_history(&g));
2193
2194  owl_editwin_set_locktext(tw, "command: ");
2195  owl_global_set_needrefresh(&g);
2196
2197  j=strlen(line);
2198  for (i=0; i<j; i++) {
2199    owl_editwin_process_char(tw, line[i]);
2200  }
2201  owl_editwin_redisplay(tw, 0);
2202
2203  owl_context_set_editline(owl_global_get_context(&g), tw);
2204  owl_function_activate_keymap("editline");
2205}
2206
2207void owl_function_start_question(char *line)
2208{
2209  owl_editwin *tw;
2210
2211  tw=owl_global_get_typwin(&g);
2212  owl_global_set_typwin_active(&g);
2213  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
2214
2215  owl_editwin_set_locktext(tw, line);
2216  owl_global_set_needrefresh(&g);
2217
2218  owl_editwin_redisplay(tw, 0);
2219
2220  owl_context_set_editresponse(owl_global_get_context(&g), tw);
2221  owl_function_activate_keymap("editresponse");
2222}
2223
2224void owl_function_start_password(char *line)
2225{
2226  owl_editwin *tw;
2227
2228  tw=owl_global_get_typwin(&g);
2229  owl_global_set_typwin_active(&g);
2230  owl_editwin_new_style(tw, OWL_EDITWIN_STYLE_ONELINE, owl_global_get_cmd_history(&g));
2231  owl_editwin_set_echochar(tw, '*');
2232
2233  owl_editwin_set_locktext(tw, line);
2234  owl_global_set_needrefresh(&g);
2235
2236  owl_editwin_redisplay(tw, 0);
2237
2238  owl_context_set_editresponse(owl_global_get_context(&g), tw);
2239  owl_function_activate_keymap("editresponse");
2240}
2241
2242char *owl_function_exec(int argc, char **argv, char *buff, int type)
2243{
2244  /* if type == 1 display in a popup
2245   * if type == 2 display an admin messages
2246   * if type == 0 return output
2247   * else display in a popup
2248   */
2249  char *newbuff, *redirect = " 2>&1 < /dev/null";
2250  char *out, buff2[1024];
2251  int size;
2252  FILE *p;
2253
2254#if OWL_STDERR_REDIR
2255  redirect = " < /dev/null";
2256#endif
2257
2258  if (argc<2) {
2259    owl_function_error("Wrong number of arguments to the exec command");
2260    return NULL;
2261  }
2262
2263  buff = skiptokens(buff, 1);
2264  newbuff = owl_malloc(strlen(buff)+strlen(redirect)+1);
2265  strcpy(newbuff, buff);
2266  strcat(newbuff, redirect);
2267
2268  if (type == 1) {
2269    owl_popexec_new(newbuff);
2270  } else {
2271    p=popen(newbuff, "r");
2272    out=owl_malloc(1024);
2273    size=1024;
2274    strcpy(out, "");
2275    while (fgets(buff2, 1024, p)!=NULL) {
2276      size+=1024;
2277      out=owl_realloc(out, size);
2278      strcat(out, buff2);
2279    }
2280    pclose(p);
2281   
2282    if (type==1) {
2283      owl_function_popless_text(out);
2284    } else if (type==0) {
2285      return out;
2286    } else if (type==2) {
2287      owl_function_adminmsg(buff, out);
2288    } else {
2289      owl_function_popless_text(out);
2290    }
2291    owl_free(out);
2292  }
2293  return NULL;
2294}
2295
2296char *owl_function_perl(int argc, char **argv, char *buff, int type)
2297{
2298  /* if type == 1 display in a popup
2299   * if type == 2 display an admin messages
2300   * if type == 0 return output
2301   * else display in a popup
2302   */
2303  char *perlout;
2304
2305  if (argc<2) {
2306    owl_function_error("Wrong number of arguments to perl command");
2307    return NULL;
2308  }
2309
2310  /* consume first token (argv[0]) */
2311  buff = skiptokens(buff, 1);
2312
2313  perlout = owl_perlconfig_execute(buff);
2314  if (perlout) { 
2315    if (type==1) {
2316      owl_function_popless_text(perlout);
2317    } else if (type==2) {
2318      owl_function_adminmsg(buff, perlout);
2319    } else if (type==0) {
2320      return perlout;
2321    } else {
2322      owl_function_popless_text(perlout);
2323    }
2324    owl_free(perlout);
2325  }
2326  return NULL;
2327}
2328
2329/* Change the filter associated with the current view.
2330 * This also figures out which message in the new filter
2331 * should have the pointer.
2332 */
2333void owl_function_change_currentview_filter(char *filtname)
2334{
2335  owl_view *v;
2336  owl_filter *f;
2337  int curid=-1, newpos, curmsg;
2338  owl_message *curm=NULL;
2339
2340  v=owl_global_get_current_view(&g);
2341
2342  curmsg=owl_global_get_curmsg(&g);
2343  if (curmsg==-1) {
2344    owl_function_debugmsg("Hit the curmsg==-1 case in change_view");
2345  } else {
2346    curm=owl_view_get_element(v, curmsg);
2347    if (curm) {
2348      curid=owl_message_get_id(curm);
2349      owl_view_save_curmsgid(v, curid);
2350    }
2351  }
2352
2353  f=owl_global_get_filter(&g, filtname);
2354  if (!f) {
2355    owl_function_error("Unknown filter %s", filtname);
2356    return;
2357  }
2358
2359  owl_view_new_filter(v, f);
2360
2361  /* Figure out what to set the current message to.
2362   * - If the view we're leaving has messages in it, go to the closest message
2363   *   to the last message pointed to in that view.
2364   * - If the view we're leaving is empty, try to restore the position
2365   *   from the last time we were in the new view.  */
2366  if (curm) {
2367    newpos = owl_view_get_nearest_to_msgid(v, curid);
2368  } else {
2369    newpos = owl_view_get_nearest_to_saved(v);
2370  }
2371
2372  owl_global_set_curmsg(&g, newpos);
2373  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
2374  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2375  owl_global_set_direction_downwards(&g);
2376}
2377
2378/* Create a new filter, or replace an existing one
2379 * with a new definition.
2380 */
2381void owl_function_create_filter(int argc, char **argv)
2382{
2383  owl_filter *f;
2384  owl_view *v;
2385  int ret, inuse=0;
2386
2387  if (argc < 2) {
2388    owl_function_error("Wrong number of arguments to filter command");
2389    return;
2390  }
2391
2392  owl_function_debugmsg("owl_function_create_filter: starting to create filter named %s", argv[1]);
2393
2394  v=owl_global_get_current_view(&g);
2395
2396  /* don't touch the all filter */
2397  if (!strcmp(argv[1], "all")) {
2398    owl_function_error("You may not change the 'all' filter.");
2399    return;
2400  }
2401
2402  /* deal with the case of trying change the filter color */
2403  if (argc==4 && !strcmp(argv[2], "-c")) {
2404    f=owl_global_get_filter(&g, argv[1]);
2405    if (!f) {
2406      owl_function_error("The filter '%s' does not exist.", argv[1]);
2407      return;
2408    }
2409    if (owl_util_string_to_color(argv[3])==-1) {
2410      owl_function_error("The color '%s' is not available.", argv[3]);
2411      return;
2412    }
2413    owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
2414    owl_global_set_needrefresh(&g);
2415    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2416    return;
2417  }
2418  if (argc==4 && !strcmp(argv[2], "-b")) {
2419    f=owl_global_get_filter(&g, argv[1]);
2420    if (!f) {
2421      owl_function_error("The filter '%s' does not exist.", argv[1]);
2422      return;
2423    }
2424    if (owl_util_string_to_color(argv[3])==-1) {
2425      owl_function_error("The color '%s' is not available.", argv[3]);
2426      return;
2427    }
2428    owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
2429    owl_global_set_needrefresh(&g);
2430    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2431    return;
2432  }
2433
2434  /* create the filter and check for errors */
2435  f=owl_malloc(sizeof(owl_filter));
2436  ret=owl_filter_init(f, argv[1], argc-2, argv+2);
2437  if (ret==-1) {
2438    owl_free(f);
2439    owl_function_error("Invalid filter");
2440    return;
2441  }
2442
2443  /* if the named filter is in use by the current view, remember it */
2444  if (!strcmp(owl_view_get_filtname(v), argv[1])) {
2445    inuse=1;
2446  }
2447
2448  /* if the named filter already exists, nuke it */
2449  if (owl_global_get_filter(&g, argv[1])) {
2450    owl_global_remove_filter(&g, argv[1]);
2451  }
2452
2453  /* add the filter */
2454  owl_global_add_filter(&g, f);
2455
2456  /* if it was in use by the current view then update */
2457  if (inuse) {
2458    owl_function_change_currentview_filter(argv[1]);
2459  }
2460  owl_global_set_needrefresh(&g);
2461  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2462}
2463
2464/* If 'filtername' does not start with 'not-' create a filter named
2465 * 'not-<filtername>' defined as "not filter <filtername>".  If the
2466 * filter 'not-<filtername>' already exists, do not overwrite it.  If
2467 * 'filtername' begins with 'not-' and a filter 'filtername' already
2468 * exists, then do nothing.  If the filter 'filtername' does not
2469 * exist, create it and define it as 'not filter <filtername>'
2470 *
2471 * Returns the name of the negated filter, which the caller must free.
2472 */
2473char *owl_function_create_negative_filter(char *filtername)
2474{
2475  char *newname;
2476  owl_filter *tmpfilt;
2477  char *argv[5];
2478
2479  owl_function_debugmsg("owl_function_create_negative_filter");
2480 
2481  if (!strncmp(filtername, "not-", 4)) {
2482    newname=owl_strdup(filtername+4);
2483  } else {
2484    newname=owl_sprintf("not-%s", filtername);
2485  }
2486
2487  tmpfilt=owl_global_get_filter(&g, newname);
2488  if (!tmpfilt) {
2489    argv[0]="filter"; /* anything is fine here */
2490    argv[1]=newname;
2491    argv[2]="not";
2492    argv[3]="filter";
2493    argv[4]=filtername;
2494    owl_function_create_filter(5, argv);
2495  }
2496
2497  owl_function_debugmsg("owl_function_create_negative_filter: returning with %s", newname);
2498  return(newname);
2499}
2500
2501void owl_function_show_filters()
2502{
2503  owl_list *l;
2504  owl_filter *f;
2505  int i, j;
2506  owl_fmtext fm;
2507
2508  owl_fmtext_init_null(&fm);
2509
2510  l=owl_global_get_filterlist(&g);
2511  j=owl_list_get_size(l);
2512
2513  owl_fmtext_append_bold(&fm, "Filters:\n");
2514
2515  for (i=0; i<j; i++) {
2516    f=owl_list_get_element(l, i);
2517    owl_fmtext_append_normal(&fm, "   ");
2518    if (owl_global_get_hascolors(&g)) {
2519      owl_fmtext_append_normal_color(&fm, owl_filter_get_name(f), owl_filter_get_fgcolor(f), owl_filter_get_bgcolor(f));
2520    } else {
2521      owl_fmtext_append_normal(&fm, owl_filter_get_name(f));
2522    }
2523    owl_fmtext_append_normal(&fm, "\n");
2524  }
2525  owl_function_popless_fmtext(&fm);
2526  owl_fmtext_free(&fm);
2527}
2528
2529void owl_function_show_filter(char *name)
2530{
2531  owl_filter *f;
2532  char buff[5000];
2533
2534  f=owl_global_get_filter(&g, name);
2535  if (!f) {
2536    owl_function_error("There is no filter named %s", name);
2537    return;
2538  }
2539  owl_filter_print(f, buff);
2540  owl_function_popless_text(buff);
2541}
2542
2543void owl_function_show_zpunts()
2544{
2545  owl_filter *f;
2546  owl_list *fl;
2547  char buff[5000];
2548  owl_fmtext fm;
2549  int i, j;
2550
2551  owl_fmtext_init_null(&fm);
2552
2553  fl=owl_global_get_puntlist(&g);
2554  j=owl_list_get_size(fl);
2555  owl_fmtext_append_bold(&fm, "Active zpunt filters:\n");
2556
2557  for (i=0; i<j; i++) {
2558    f=owl_list_get_element(fl, i);
2559    owl_filter_print(f, buff);
2560    owl_fmtext_append_normal(&fm, buff);
2561  }
2562  owl_function_popless_fmtext(&fm);
2563  owl_fmtext_free(&fm);
2564}
2565
2566/* Create a filter for a class, instance if one doesn't exist.  If
2567 * instance is NULL then catch all messgaes in the class.  Returns the
2568 * name of the filter, which the caller must free.
2569 */
2570char *owl_function_classinstfilt(char *c, char *i) 
2571{
2572  owl_list *fl;
2573  owl_filter *f;
2574  char *argbuff, *filtname;
2575  char *tmpclass, *tmpinstance = NULL;
2576  char *class, *instance = NULL;
2577  int len;
2578
2579  class = owl_util_baseclass(c);
2580  if(i) {
2581    instance = owl_util_baseclass(i);
2582  }
2583
2584  fl=owl_global_get_filterlist(&g);
2585
2586  /* name for the filter */
2587  len=strlen(class)+30;
2588  if (instance) len+=strlen(instance);
2589  filtname=owl_malloc(len);
2590  if (!instance) {
2591    sprintf(filtname, "class-%s", class);
2592  } else {
2593    sprintf(filtname, "class-%s-instance-%s", class, instance);
2594  }
2595  /* downcase it */
2596  downstr(filtname);
2597  /* turn spaces, single quotes, and double quotes into dots */
2598  owl_text_tr(filtname, ' ', '.');
2599  owl_text_tr(filtname, '\'', '.');
2600  owl_text_tr(filtname, '"', '.');
2601 
2602  /* if it already exists then go with it.  This lets users override */
2603  if (owl_global_get_filter(&g, filtname)) {
2604    return(filtname);
2605  }
2606
2607  /* create the new filter */
2608  tmpclass=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2609  owl_text_tr(tmpclass, ' ', '.');
2610  owl_text_tr(tmpclass, '\'', '.');
2611  owl_text_tr(tmpclass, '"', '.');
2612  if (instance) {
2613    tmpinstance=owl_text_quote(instance, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2614    owl_text_tr(tmpinstance, ' ', '.');
2615    owl_text_tr(tmpinstance, '\'', '.');
2616    owl_text_tr(tmpinstance, '"', '.');
2617  }
2618  len = strlen(tmpclass);
2619  if(tmpinstance) len += strlen(tmpinstance);
2620  len += 60;
2621  argbuff = owl_malloc(len);
2622  sprintf(argbuff, "class ^(un)*%s(\\.d)*$", tmpclass);
2623  if (tmpinstance) {
2624    sprintf(argbuff, "%s and ( instance ^%s(\\.d)*$ )", argbuff, tmpinstance);
2625  }
2626  owl_free(tmpclass);
2627  if (tmpinstance) owl_free(tmpinstance);
2628
2629  f=owl_malloc(sizeof(owl_filter));
2630  owl_filter_init_fromstring(f, filtname, argbuff);
2631
2632  /* add it to the global list */
2633  owl_global_add_filter(&g, f);
2634
2635  owl_free(argbuff);
2636  owl_free(class);
2637  if (instance) {
2638    owl_free(instance);
2639  }
2640  return(filtname);
2641}
2642
2643/* Create a filter for personal zephyrs to or from the specified
2644 * zephyr user.  Includes login/logout notifications for the user.
2645 * The name of the filter will be 'user-<user>'.  If a filter already
2646 * exists with this name, no new filter will be created.  This allows
2647 * the configuration to override this function.  Returns the name of
2648 * the filter, which the caller must free.
2649 */
2650char *owl_function_zuserfilt(char *user)
2651{
2652  owl_filter *f;
2653  char *argbuff, *longuser, *shortuser, *filtname;
2654
2655  /* stick the local realm on if it's not there */
2656  longuser=long_zuser(user);
2657  shortuser=short_zuser(user);
2658
2659  /* name for the filter */
2660  filtname=owl_malloc(strlen(shortuser)+20);
2661  sprintf(filtname, "user-%s", shortuser);
2662
2663  /* if it already exists then go with it.  This lets users override */
2664  if (owl_global_get_filter(&g, filtname)) {
2665    return(owl_strdup(filtname));
2666  }
2667
2668  /* create the new-internal filter */
2669  f=owl_malloc(sizeof(owl_filter));
2670
2671  argbuff=owl_malloc(strlen(longuser)+1000);
2672  sprintf(argbuff, "( type ^zephyr$ and ( class ^message$ and instance ^personal$ and ");
2673  sprintf(argbuff, "%s ( ( direction ^in$ and sender ^%s$ ) or ( direction ^out$ and recipient ^%s$ ) ) )", argbuff, longuser, longuser);
2674  sprintf(argbuff, "%s or ( ( class ^login$ ) and ( sender ^%s$ ) ) )", argbuff, longuser);
2675
2676  owl_filter_init_fromstring(f, filtname, argbuff);
2677
2678  /* add it to the global list */
2679  owl_global_add_filter(&g, f);
2680
2681  /* free stuff */
2682  owl_free(argbuff);
2683  owl_free(longuser);
2684  owl_free(shortuser);
2685
2686  return(filtname);
2687}
2688
2689/* Create a filter for AIM IM messages to or from the specified
2690 * screenname.  The name of the filter will be 'aimuser-<user>'.  If a
2691 * filter already exists with this name, no new filter will be
2692 * created.  This allows the configuration to override this function.
2693 * Returns the name of the filter, which the caller must free.
2694 */
2695char *owl_function_aimuserfilt(char *user)
2696{
2697  owl_filter *f;
2698  char *argbuff, *filtname;
2699  char *escuser;
2700
2701  /* name for the filter */
2702  filtname=owl_malloc(strlen(user)+40);
2703  sprintf(filtname, "aimuser-%s", user);
2704
2705  /* if it already exists then go with it.  This lets users override */
2706  if (owl_global_get_filter(&g, filtname)) {
2707    return(owl_strdup(filtname));
2708  }
2709
2710  /* create the new-internal filter */
2711  f=owl_malloc(sizeof(owl_filter));
2712
2713  escuser = owl_text_quote(user, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
2714
2715  argbuff=owl_malloc(1000);
2716  sprintf(argbuff,
2717          "( type ^aim$ and ( ( sender ^%s$ and recipient ^%s$ ) or ( sender ^%s$ and recipient ^%s$ ) ) )",
2718          escuser, owl_global_get_aim_screenname(&g), owl_global_get_aim_screenname(&g), escuser);
2719
2720  owl_filter_init_fromstring(f, filtname, argbuff);
2721
2722  /* add it to the global list */
2723  owl_global_add_filter(&g, f);
2724
2725  /* free stuff */
2726  owl_free(argbuff);
2727  owl_free(escuser);
2728
2729  return(filtname);
2730}
2731
2732char *owl_function_typefilt(char *type)
2733{
2734  owl_filter *f;
2735  char *argbuff, *filtname;
2736
2737  /* name for the filter */
2738  filtname=owl_sprintf("type-%s", type);
2739
2740  /* if it already exists then go with it.  This lets users override */
2741  if (owl_global_get_filter(&g, filtname)) {
2742    return filtname;
2743  }
2744
2745  /* create the new-internal filter */
2746  f=owl_malloc(sizeof(owl_filter));
2747
2748  argbuff = owl_sprintf("type ^%s$", type);
2749
2750  owl_filter_init_fromstring(f, filtname, argbuff);
2751
2752  /* add it to the global list */
2753  owl_global_add_filter(&g, f);
2754
2755  /* free stuff */
2756  owl_free(argbuff);
2757
2758  return filtname;
2759}
2760
2761/* If flag is 1, marks for deletion.  If flag is 0,
2762 * unmarks for deletion. */
2763void owl_function_delete_curview_msgs(int flag)
2764{
2765  owl_view *v;
2766  int i, j;
2767
2768  v=owl_global_get_current_view(&g);
2769  j=owl_view_get_size(v);
2770  for (i=0; i<j; i++) {
2771    if (flag == 1) {
2772      owl_message_mark_delete(owl_view_get_element(v, i));
2773    } else if (flag == 0) {
2774      owl_message_unmark_delete(owl_view_get_element(v, i));
2775    }
2776  }
2777
2778  owl_function_makemsg("%i messages marked for %sdeletion", j, flag?"":"un");
2779
2780  owl_mainwin_redisplay(owl_global_get_mainwin(&g)); 
2781}
2782
2783/* Create a filter based on the current message.  Returns the name of
2784 * a filter or null.  The caller must free this name.
2785 *
2786 * if the curmsg is a personal zephyr return a filter name
2787 *    to the zephyr converstaion with that user.
2788 * If the curmsg is a zephyr class message, instance foo, recip *,
2789 *    return a filter name to the class, inst.
2790 * If the curmsg is a zephyr class message and type==0 then
2791 *    return a filter name for just the class.
2792 * If the curmsg is a zephyr class message and type==1 then
2793 *    return a filter name for the class and instance.
2794 * If the curmsg is a personal AIM message returna  filter
2795 *    name to the AIM conversation with that user
2796 */
2797char *owl_function_smartfilter(int type)
2798{
2799  owl_view *v;
2800  owl_message *m;
2801  char *zperson, *filtname=NULL;
2802 
2803  v=owl_global_get_current_view(&g);
2804  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2805
2806  if (!m || owl_view_get_size(v)==0) {
2807    owl_function_error("No message selected\n");
2808    return(NULL);
2809  }
2810
2811  /* very simple handling of admin messages for now */
2812  if (owl_message_is_type_admin(m)) {
2813    return(owl_function_typefilt("admin"));
2814  }
2815
2816  /* very simple handling of loopback messages for now */
2817  if (owl_message_is_type_loopback(m)) {
2818    return(owl_function_typefilt("loopback"));
2819  }
2820
2821  /* aim messages */
2822  if (owl_message_is_type_aim(m)) {
2823    if (owl_message_is_direction_in(m)) {
2824      filtname=owl_function_aimuserfilt(owl_message_get_sender(m));
2825    } else if (owl_message_is_direction_out(m)) {
2826      filtname=owl_function_aimuserfilt(owl_message_get_recipient(m));
2827    }
2828    return(filtname);
2829  }
2830
2831  /* narrow personal and login messages to the sender or recip as appropriate */
2832  if (owl_message_is_type_zephyr(m)) {
2833    if (owl_message_is_private(m) || owl_message_is_loginout(m)) {
2834      if (owl_message_is_direction_in(m)) {
2835        zperson=short_zuser(owl_message_get_sender(m));
2836      } else {
2837        zperson=short_zuser(owl_message_get_recipient(m));
2838      }
2839      filtname=owl_function_zuserfilt(zperson);
2840      owl_free(zperson);
2841      return(filtname);
2842    }
2843
2844    /* narrow class MESSAGE, instance foo, recip * messages to class, inst */
2845    if (!strcasecmp(owl_message_get_class(m), "message")) {
2846      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
2847      return(filtname);
2848    }
2849
2850    /* otherwise narrow to the class */
2851    if (type==0) {
2852      filtname=owl_function_classinstfilt(owl_message_get_class(m), NULL);
2853    } else if (type==1) {
2854      filtname=owl_function_classinstfilt(owl_message_get_class(m), owl_message_get_instance(m));
2855    }
2856    return(filtname);
2857  }
2858
2859  /* pass it off to perl */
2860  char *argv[1];
2861  if(type) {
2862    argv[0] = "-i";
2863  };
2864  return owl_perlconfig_message_call_method(m, "smartfilter", type ? 1 : 0, argv);
2865}
2866
2867void owl_function_smartzpunt(int type)
2868{
2869  /* Starts a zpunt command based on the current class,instance pair.
2870   * If type=0, uses just class.  If type=1, uses instance as well. */
2871  owl_view *v;
2872  owl_message *m;
2873  char *cmd, *cmdprefix, *mclass, *minst;
2874 
2875  v=owl_global_get_current_view(&g);
2876  m=owl_view_get_element(v, owl_global_get_curmsg(&g));
2877
2878  if (!m || owl_view_get_size(v)==0) {
2879    owl_function_error("No message selected\n");
2880    return;
2881  }
2882
2883  /* for now we skip admin messages. */
2884  if (owl_message_is_type_admin(m)
2885      || owl_message_is_loginout(m)
2886      || !owl_message_is_type_zephyr(m)) {
2887    owl_function_error("smartzpunt doesn't support this message type.");
2888    return;
2889  }
2890
2891  mclass = owl_message_get_class(m);
2892  minst = owl_message_get_instance(m);
2893  if (!mclass || !*mclass || *mclass==' '
2894      || (!strcasecmp(mclass, "message") && !strcasecmp(minst, "personal"))
2895      || (type && (!minst || !*minst|| *minst==' '))) {
2896    owl_function_error("smartzpunt can't safely do this for <%s,%s>",
2897                         mclass, minst);
2898  } else {
2899    cmdprefix = "start-command zpunt ";
2900    cmd = owl_malloc(strlen(cmdprefix)+strlen(mclass)+strlen(minst)+10);
2901    strcpy(cmd, cmdprefix);
2902    strcat(cmd, owl_getquoting(mclass));
2903    strcat(cmd, mclass);
2904    strcat(cmd, owl_getquoting(mclass));
2905    if (type) {
2906      strcat(cmd, " ");
2907      strcat(cmd, owl_getquoting(minst));
2908      strcat(cmd, minst);
2909      strcat(cmd, owl_getquoting(minst));
2910    } else {
2911      strcat(cmd, " *");
2912    }
2913    owl_function_command(cmd);
2914    owl_free(cmd);
2915  }
2916}
2917
2918/* Set the color of the current view's filter to
2919 * be 'color'
2920 */
2921void owl_function_color_current_filter(char *fgcolor, char *bgcolor)
2922{
2923  char *name;
2924
2925  name=owl_view_get_filtname(owl_global_get_current_view(&g));
2926  owl_function_color_filter(name, fgcolor, bgcolor);
2927}
2928
2929/* Set the color of the filter 'filter' to be 'color'.  If the color
2930 * name does not exist, return -1, if the filter does not exist or is
2931 * the "all" filter, return -2.  Return 0 on success
2932 */
2933int owl_function_color_filter(char *filtname, char *fgcolor, char *bgcolor)
2934{
2935  owl_filter *f;
2936
2937  f=owl_global_get_filter(&g, filtname);
2938  if (!f) {
2939    owl_function_error("Unknown filter");
2940    return(-2);
2941  }
2942
2943  /* don't touch the all filter */
2944  if (!strcmp(filtname, "all")) {
2945    owl_function_error("You may not change the 'all' filter.");
2946    return(-2);
2947  }
2948
2949  if (owl_util_string_to_color(fgcolor)==-1) {
2950    owl_function_error("No color named '%s' avilable.", fgcolor);
2951    return(-1);
2952  }
2953
2954
2955  if (bgcolor != NULL) {
2956    if (owl_util_string_to_color(bgcolor)==-1) {
2957      owl_function_error("No color named '%s' avilable.", bgcolor);
2958      return(-1);
2959    }
2960    owl_filter_set_bgcolor(f, owl_util_string_to_color(bgcolor));
2961  }
2962  owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
2963 
2964  owl_global_set_needrefresh(&g);
2965  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
2966  return(0);
2967}
2968
2969void owl_function_show_colors()
2970{
2971  owl_fmtext fm;
2972  int i; 
2973 
2974  owl_fmtext_init_null(&fm);
2975  owl_fmtext_append_normal(&fm, "default: ");
2976  owl_fmtext_append_normal_color(&fm, "default\n", OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
2977
2978  owl_fmtext_append_normal(&fm,"red:      ");
2979  owl_fmtext_append_normal_color(&fm, "red\n", OWL_COLOR_RED, OWL_COLOR_DEFAULT);
2980
2981  owl_fmtext_append_normal(&fm,"green:    ");
2982  owl_fmtext_append_normal_color(&fm, "green\n", OWL_COLOR_GREEN, OWL_COLOR_DEFAULT);
2983
2984  owl_fmtext_append_normal(&fm,"yellow:   ");
2985  owl_fmtext_append_normal_color(&fm, "yellow\n", OWL_COLOR_YELLOW, OWL_COLOR_DEFAULT);
2986
2987  owl_fmtext_append_normal(&fm,"blue:     ");
2988  owl_fmtext_append_normal_color(&fm, "blue\n", OWL_COLOR_BLUE, OWL_COLOR_DEFAULT);
2989
2990  owl_fmtext_append_normal(&fm,"magenta:  ");
2991  owl_fmtext_append_normal_color(&fm, "magenta\n", OWL_COLOR_MAGENTA, OWL_COLOR_DEFAULT);
2992
2993  owl_fmtext_append_normal(&fm,"cyan:     ");
2994  owl_fmtext_append_normal_color(&fm, "cyan\n", OWL_COLOR_CYAN, OWL_COLOR_DEFAULT);
2995
2996  owl_fmtext_append_normal(&fm,"white:    ");
2997  owl_fmtext_append_normal_color(&fm, "white\n", OWL_COLOR_WHITE, OWL_COLOR_DEFAULT);
2998
2999  for(i = 8; i < COLORS; ++i) {
3000    char* str1 = owl_sprintf("%4i:     ",i);
3001    char* str2 = owl_sprintf("%i\n",i);
3002    owl_fmtext_append_normal(&fm,str1);
3003    owl_fmtext_append_normal_color(&fm, str2, i, OWL_COLOR_DEFAULT);
3004    owl_free(str1);
3005     owl_free(str2);
3006  }
3007 
3008  owl_function_popless_fmtext(&fm);
3009  owl_fmtext_free(&fm);
3010}
3011
3012/* add the given class, inst, recip to the punt list for filtering.
3013 *   if direction==0 then punt
3014 *   if direction==1 then unpunt
3015 */
3016void owl_function_zpunt(char *class, char *inst, char *recip, int direction)
3017{
3018  owl_filter *f;
3019  owl_list *fl;
3020  char *buff;
3021  char *quoted;
3022  int ret, i, j;
3023
3024  fl=owl_global_get_puntlist(&g);
3025
3026  /* first, create the filter */
3027  f=malloc(sizeof(owl_filter));
3028  buff=malloc(strlen(class)+strlen(inst)+strlen(recip)+100);
3029  strcpy(buff, "class");
3030  if (!strcmp(class, "*")) {
3031    strcat(buff, " .*");
3032  } else {
3033    quoted=owl_text_quote(class, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
3034    owl_text_tr(quoted, ' ', '.');
3035    owl_text_tr(quoted, '\'', '.');
3036    owl_text_tr(quoted, '"', '.');
3037    sprintf(buff, "%s ^(un)*%s(\\.d)*$", buff, quoted);
3038    owl_free(quoted);
3039  }
3040  if (!strcmp(inst, "*")) {
3041    strcat(buff, " and instance .*");
3042  } else {
3043    quoted=owl_text_quote(inst, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
3044    owl_text_tr(quoted, ' ', '.');
3045    owl_text_tr(quoted, '\'', '.');
3046    owl_text_tr(quoted, '"', '.');
3047    sprintf(buff, "%s and instance ^(un)*%s(\\.d)*$", buff, quoted);
3048    owl_free(quoted);
3049  }
3050  if (strcmp(recip, "*")) {
3051    quoted=owl_text_quote(recip, OWL_REGEX_QUOTECHARS, OWL_REGEX_QUOTEWITH);
3052    owl_text_tr(quoted, ' ', '.');
3053    owl_text_tr(quoted, '\'', '.');
3054    owl_text_tr(quoted, '"', '.');
3055    sprintf(buff, "%s and recipient ^%s$", buff, quoted);
3056    owl_free(quoted);
3057  }
3058 
3059  owl_function_debugmsg("About to filter %s", buff);
3060  ret=owl_filter_init_fromstring(f, "punt-filter", buff);
3061  owl_free(buff);
3062  if (ret) {
3063    owl_function_error("Error creating filter for zpunt");
3064    owl_filter_free(f);
3065    return;
3066  }
3067
3068  /* Check for an identical filter */
3069  j=owl_list_get_size(fl);
3070  for (i=0; i<j; i++) {
3071    if (owl_filter_equiv(f, owl_list_get_element(fl, i))) {
3072      /* if we're punting, then just silently bow out on this duplicate */
3073      if (direction==0) {
3074        owl_filter_free(f);
3075        return;
3076      }
3077
3078      /* if we're unpunting, then remove this filter from the puntlist */
3079      if (direction==1) {
3080        owl_filter_free(owl_list_get_element(fl, i));
3081        owl_list_remove_element(fl, i);
3082        return;
3083      }
3084    }
3085  }
3086
3087  /* If we're punting, add the filter to the global punt list */
3088  if (direction==0) {
3089    owl_list_append_element(fl, f);
3090  }
3091}
3092
3093void owl_function_activate_keymap(char *keymap)
3094{
3095  if (!owl_keyhandler_activate(owl_global_get_keyhandler(&g), keymap)) {
3096    owl_function_error("Unable to activate keymap '%s'", keymap);
3097  }
3098}
3099
3100void owl_function_show_keymaps()
3101{
3102  owl_list l;
3103  owl_fmtext fm;
3104  owl_keymap *km;
3105  owl_keyhandler *kh;
3106  int i, numkm;
3107  char *kmname;
3108
3109  kh = owl_global_get_keyhandler(&g);
3110  owl_fmtext_init_null(&fm);
3111  owl_fmtext_append_bold(&fm, "Keymaps:   ");
3112  owl_fmtext_append_normal(&fm, "(use 'show keymap <name>' for details)\n");
3113  owl_keyhandler_get_keymap_names(kh, &l);
3114  owl_fmtext_append_list(&fm, &l, "\n", owl_function_keymap_summary);
3115  owl_fmtext_append_normal(&fm, "\n");
3116
3117  numkm = owl_list_get_size(&l);
3118  for (i=0; i<numkm; i++) {
3119    kmname = owl_list_get_element(&l, i);
3120    km = owl_keyhandler_get_keymap(kh, kmname);
3121    owl_fmtext_append_bold(&fm, "\n\n----------------------------------------------------------------------------------------------------\n\n");
3122    owl_keymap_get_details(km, &fm);   
3123  }
3124  owl_fmtext_append_normal(&fm, "\n");
3125 
3126  owl_function_popless_fmtext(&fm);
3127  owl_keyhandler_keymap_namelist_free(&l);
3128  owl_fmtext_free(&fm);
3129}
3130
3131char *owl_function_keymap_summary(void *name)
3132{
3133  owl_keymap *km
3134    = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
3135  if (km) return owl_keymap_summary(km);
3136  else return(NULL);
3137}
3138
3139/* TODO: implement for real */
3140void owl_function_show_keymap(char *name)
3141{
3142  owl_fmtext fm;
3143  owl_keymap *km;
3144
3145  owl_fmtext_init_null(&fm);
3146  km = owl_keyhandler_get_keymap(owl_global_get_keyhandler(&g), name);
3147  if (km) {
3148    owl_keymap_get_details(km, &fm);
3149  } else {
3150    owl_fmtext_append_normal(&fm, "No such keymap...\n");
3151  } 
3152  owl_function_popless_fmtext(&fm);
3153  owl_fmtext_free(&fm);
3154}
3155
3156void owl_function_help_for_command(char *cmdname)
3157{
3158  owl_fmtext fm;
3159
3160  owl_fmtext_init_null(&fm);
3161  owl_cmd_get_help(owl_global_get_cmddict(&g), cmdname, &fm);
3162  owl_function_popless_fmtext(&fm); 
3163  owl_fmtext_free(&fm);
3164}
3165
3166void owl_function_search_start(char *string, int direction)
3167{
3168  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
3169  owl_global_set_search_active(&g, string);
3170  owl_function_search_helper(0, direction);
3171}
3172
3173void owl_function_search_continue(int direction)
3174{
3175  /* direction is OWL_DIRECTION_DOWNWARDS or OWL_DIRECTION_UPWARDS */
3176  owl_function_search_helper(1, direction);
3177}
3178
3179void owl_function_search_helper(int mode, int direction)
3180{
3181  /* move to a message that contains the string.  If direction is
3182   * OWL_DIRECTION_DOWNWARDS then search fowards, if direction is
3183   * OWL_DIRECTION_UPWARDS then search backwards.
3184   *
3185   * If mode==0 then it will stay on the current message if it
3186   * contains the string.
3187   */
3188
3189  owl_view *v;
3190  int viewsize, i, curmsg, start;
3191  owl_message *m;
3192
3193  v=owl_global_get_current_view(&g);
3194  viewsize=owl_view_get_size(v);
3195  curmsg=owl_global_get_curmsg(&g);
3196 
3197  if (viewsize==0) {
3198    owl_function_error("No messages present");
3199    return;
3200  }
3201
3202  if (mode==0) {
3203    start=curmsg;
3204  } else if (direction==OWL_DIRECTION_DOWNWARDS) {
3205    start=curmsg+1;
3206  } else {
3207    start=curmsg-1;
3208  }
3209
3210  /* bounds check */
3211  if (start>=viewsize || start<0) {
3212    owl_function_error("No further matches found");
3213    return;
3214  }
3215
3216  for (i=start; i<viewsize && i>=0;) {
3217    m=owl_view_get_element(v, i);
3218    if (owl_message_search(m, owl_global_get_search_string(&g))) {
3219      owl_global_set_curmsg(&g, i);
3220      owl_function_calculate_topmsg(direction);
3221      owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3222      if (direction==OWL_DIRECTION_DOWNWARDS) {
3223        owl_global_set_direction_downwards(&g);
3224      } else {
3225        owl_global_set_direction_upwards(&g);
3226      }
3227      return;
3228    }
3229    if (direction==OWL_DIRECTION_DOWNWARDS) {
3230      i++;
3231    } else {
3232      i--;
3233    }
3234  }
3235  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3236  owl_function_error("No matches found");
3237}
3238
3239/* strips formatting from ztext and returns the unformatted text.
3240 * caller is responsible for freeing. */
3241char *owl_function_ztext_stylestrip(char *zt)
3242{
3243  owl_fmtext fm;
3244  char *plaintext;
3245
3246  owl_fmtext_init_null(&fm);
3247  owl_fmtext_append_ztext(&fm, zt);
3248  plaintext = owl_fmtext_print_plain(&fm);
3249  owl_fmtext_free(&fm);
3250  return(plaintext);
3251}
3252
3253/* Popup a buddylisting.  If filename is NULL use the default .anyone */
3254void owl_function_buddylist(int aim, int zephyr, char *filename)
3255{
3256  int i, j, x, idle;
3257  owl_fmtext fm;
3258  owl_buddylist *bl;
3259  owl_buddy *b;
3260  owl_list anyone;
3261  char *foo, *timestr;
3262#ifdef HAVE_LIBZEPHYR
3263  char *tmp, *user, *line;
3264  ZLocations_t location[200];
3265  int numlocs, ret;
3266#endif
3267
3268  owl_fmtext_init_null(&fm);
3269
3270  /* AIM first */
3271  if (aim && owl_global_is_aimloggedin(&g)) {
3272    bl=owl_global_get_buddylist(&g);
3273
3274    owl_fmtext_append_bold(&fm, "AIM users logged in:\n");
3275    /* we're assuming AIM for now */
3276    j=owl_buddylist_get_size(bl);
3277    for (i=0; i<j; i++) {
3278      b=owl_buddylist_get_buddy_n(bl, i);
3279      idle=owl_buddy_get_idle_time(b);
3280      if (idle!=0) {
3281        timestr=owl_util_minutes_to_timestr(idle);
3282      } else {
3283        timestr=owl_strdup("");
3284      }
3285      foo=owl_sprintf("  %-20.20s %-12.12s\n", owl_buddy_get_name(b), timestr);
3286      owl_fmtext_append_normal(&fm, foo);
3287      owl_free(timestr);
3288      owl_free(foo);
3289    }
3290  }
3291
3292#ifdef HAVE_LIBZEPHYR
3293  if (zephyr) {
3294    if(!owl_global_is_havezephyr(&g)) {
3295      owl_function_error("Zephyr currently not available.");
3296    } else {
3297      owl_fmtext_append_bold(&fm, "Zephyr users logged in:\n");
3298      owl_list_create(&anyone);
3299      ret=owl_zephyr_get_anyone_list(&anyone, filename);
3300      if (ret) {
3301        owl_fmtext_append_normal(&fm, "  Error opening file for zephyr buddies.\n");
3302      } else {
3303        j=owl_list_get_size(&anyone);
3304        for (i=0; i<j; i++) {
3305          user=owl_list_get_element(&anyone, i);
3306          ret=ZLocateUser(user, &numlocs, ZAUTH);
3307          if (ret!=ZERR_NONE) {
3308            owl_function_error("Error getting location for %s", user);
3309            continue;
3310          }
3311
3312          numlocs=200;
3313          ret=ZGetLocations(location, &numlocs);
3314          if (ret==0) {
3315            for (x=0; x<numlocs; x++) {
3316              line=malloc(strlen(location[x].host)+strlen(location[x].time)+strlen(location[x].tty)+100);
3317              tmp=short_zuser(user);
3318              sprintf(line, "  %-10.10s %-24.24s %-12.12s  %20.20s\n",
3319                      tmp,
3320                      location[x].host,
3321                      location[x].tty,
3322                      location[x].time);
3323              owl_fmtext_append_normal(&fm, line);
3324              owl_free(tmp);
3325              owl_free(line);
3326            }
3327            if (numlocs>=200) {
3328              owl_fmtext_append_normal(&fm, "  Too many locations found for this user, truncating.\n");
3329            }
3330          }
3331        }
3332      }
3333      owl_list_free_all(&anyone, owl_free);
3334    } 
3335  }
3336#endif
3337
3338  if(aim && zephyr) {
3339      if(owl_perlconfig_is_function("BarnOwl::Hooks::_get_blist")) {
3340          char * perlblist = owl_perlconfig_execute("BarnOwl::Hooks::_get_blist()");
3341          if(perlblist) {
3342              owl_fmtext_append_ztext(&fm, perlblist);
3343              owl_free(perlblist);
3344          }
3345      }
3346  }
3347 
3348  owl_function_popless_fmtext(&fm);
3349  owl_fmtext_free(&fm);
3350}
3351
3352/* Dump messages in the current view to the file 'filename'. */
3353void owl_function_dump(char *filename) 
3354{
3355  int i, j, count;
3356  owl_message *m;
3357  owl_view *v;
3358  FILE *file;
3359
3360  v=owl_global_get_current_view(&g);
3361
3362  /* in the future make it ask yes/no */
3363  /*
3364  ret=stat(filename, &sbuf);
3365  if (!ret) {
3366    ret=owl_function_askyesno("File exists, continue? [Y/n]");
3367    if (!ret) return;
3368  }
3369  */
3370
3371  file=fopen(filename, "w");
3372  if (!file) {
3373    owl_function_error("Error opening file");
3374    return;
3375  }
3376
3377  count=0;
3378  j=owl_view_get_size(v);
3379  for (i=0; i<j; i++) {
3380    m=owl_view_get_element(v, i);
3381    fputs(owl_message_get_text(m), file);
3382  }
3383  fclose(file);
3384  owl_function_makemsg("Messages dumped to %s", filename);
3385}
3386
3387void owl_function_do_newmsgproc(void)
3388{
3389  if (owl_global_get_newmsgproc(&g) && strcmp(owl_global_get_newmsgproc(&g), "")) {
3390    /* if there's a process out there, we need to check on it */
3391    if (owl_global_get_newmsgproc_pid(&g)) {
3392      owl_function_debugmsg("Checking on newmsgproc pid==%i", owl_global_get_newmsgproc_pid(&g));
3393      owl_function_debugmsg("Waitpid return is %i", waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG));
3394      waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG);
3395      if (waitpid(owl_global_get_newmsgproc_pid(&g), NULL, WNOHANG)==-1) {
3396        /* it exited */
3397        owl_global_set_newmsgproc_pid(&g, 0);
3398        owl_function_debugmsg("newmsgproc exited");
3399      } else {
3400        owl_function_debugmsg("newmsgproc did not exit");
3401      }
3402    }
3403   
3404    /* if it exited, fork & exec a new one */
3405    if (owl_global_get_newmsgproc_pid(&g)==0) {
3406      int i, myargc;
3407      i=fork();
3408      if (i) {
3409        /* parent set the child's pid */
3410        owl_global_set_newmsgproc_pid(&g, i);
3411        owl_function_debugmsg("I'm the parent and I started a new newmsgproc with pid %i", i);
3412      } else {
3413        /* child exec's the program */
3414        char **parsed;
3415        parsed=owl_parseline(owl_global_get_newmsgproc(&g), &myargc);
3416        if (myargc < 0) {
3417          owl_function_debugmsg("Could not parse newmsgproc '%s': unbalanced quotes?", owl_global_get_newmsgproc(&g));
3418        }
3419        if (myargc <= 0) {
3420          _exit(127);
3421        }
3422        parsed=realloc(parsed, sizeof(*parsed) * (myargc+1));
3423        parsed[myargc] = NULL;
3424       
3425        owl_function_debugmsg("About to exec \"%s\" with %d arguments", parsed[0], myargc);
3426       
3427        execvp(parsed[0], parsed);
3428       
3429       
3430        /* was there an error exec'ing? */
3431        owl_function_debugmsg("Cannot run newmsgproc '%s': cannot exec '%s': %s", 
3432                              owl_global_get_newmsgproc(&g), parsed[0], strerror(errno));
3433        _exit(127);
3434      }
3435    }
3436  }
3437}
3438
3439/* print the xterm escape sequence to raise the window */
3440void owl_function_xterm_raise(void)
3441{
3442  printf("\033[5t");
3443}
3444
3445/* print the xterm escape sequence to deiconify the window */
3446void owl_function_xterm_deiconify(void)
3447{
3448  printf("\033[1t");
3449}
3450
3451/* Add the specified command to the startup file.  Eventually this
3452 * should be clever, and rewriting settings that will obviosly
3453 * override earlier settings with 'set' 'bindkey' and 'alias'
3454 * commands.  For now though we just remove any line that would
3455 * duplicate this one and then append this line to the end of
3456 * startupfile.
3457 */
3458void owl_function_addstartup(char *buff)
3459{
3460  FILE *file;
3461  char *filename;
3462
3463  filename=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE);
3464  file=fopen(filename, "a");
3465  if (!file) {
3466    owl_function_error("Error opening startupfile for new command");
3467    owl_free(filename);
3468    return;
3469  }
3470
3471  /* delete earlier copies */
3472  owl_util_file_deleteline(filename, buff, 1);
3473  owl_free(filename);
3474
3475  /* add this line */
3476  fprintf(file, "%s\n", buff);
3477
3478  fclose(file);
3479}
3480
3481/* Remove the specified command from the startup file. */
3482void owl_function_delstartup(char *buff)
3483{
3484  char *filename;
3485  filename=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE);
3486  owl_util_file_deleteline(filename, buff, 1);
3487  owl_free(filename);
3488}
3489
3490/* Execute owl commands from the given filename.  If the filename
3491 * is NULL, use the default owl startup commands file.
3492 */
3493void owl_function_source(char *filename)
3494{
3495  FILE *file;
3496  char buff[LINE];
3497
3498  if (!filename) {
3499    filename=owl_sprintf("%s/%s", owl_global_get_homedir(&g), OWL_STARTUP_FILE);
3500    file=fopen(filename, "r");
3501    owl_free(filename);
3502  } else {
3503    file=fopen(filename, "r");
3504  }
3505  if (!file) {
3506    /* just fail silently if it doesn't exist */
3507    return;
3508  }
3509  while (fgets(buff, LINE, file)!=NULL) {
3510    if (buff[0] == '#') continue;
3511    buff[strlen(buff)-1]='\0';
3512    owl_function_command(buff);
3513  }
3514  fclose(file);
3515}
3516
3517void owl_function_change_style(owl_view *v, char *stylename)
3518{
3519  owl_style *s;
3520
3521  s=owl_global_get_style_by_name(&g, stylename);
3522  if (!s) {
3523    owl_function_error("No style named %s", stylename);
3524    return;
3525  }
3526  owl_view_set_style(v, s);
3527  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3528  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3529  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3530}
3531
3532void owl_function_toggleoneline()
3533{
3534  owl_view *v;
3535  owl_style *s;
3536
3537  v=owl_global_get_current_view(&g);
3538  s=owl_view_get_style(v);
3539
3540  if (!owl_style_matches_name(s, "oneline")) {
3541    owl_function_change_style(v, "oneline");
3542  } else {
3543    owl_function_change_style(v, owl_global_get_default_style(&g));
3544  }
3545
3546  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
3547  owl_function_calculate_topmsg(OWL_DIRECTION_DOWNWARDS);
3548  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
3549}
3550
3551void owl_function_error(char *fmt, ...)
3552{
3553  va_list ap;
3554  char buff[2048], buff2[2048];
3555  char *date;
3556  time_t now;
3557
3558  now=time(NULL);
3559  date=owl_strdup(ctime(&now));
3560  date[strlen(date)-1]='\0';
3561
3562  va_start(ap, fmt);
3563
3564  vsnprintf(buff, 2048, fmt, ap);
3565  sprintf(buff2, "%s %s", date, buff);
3566  owl_function_debugmsg("ERROR: %s", buff);
3567  if (owl_global_get_curs_msgwin(&g)) {
3568    werase(owl_global_get_curs_msgwin(&g));
3569    waddstr(owl_global_get_curs_msgwin(&g), buff); 
3570    wnoutrefresh(owl_global_get_curs_msgwin(&g));
3571    owl_global_set_needrefresh(&g);
3572  }
3573  owl_errqueue_append_err(owl_global_get_errqueue(&g), buff2);
3574  va_end(ap);
3575  owl_free(date);
3576}
3577
3578void owl_function_showerrs()
3579{
3580  owl_fmtext fm;
3581
3582  owl_fmtext_init_null(&fm);
3583  owl_fmtext_append_normal(&fm, "Errors:\n\n");
3584  owl_errqueue_to_fmtext(owl_global_get_errqueue(&g), &fm);
3585  owl_function_popless_fmtext(&fm);
3586}
3587
3588void owl_function_makemsg(char *fmt, ...)
3589{
3590  va_list ap;
3591  char buff[2048];
3592
3593  if (!owl_global_get_curs_msgwin(&g)) return;
3594
3595  va_start(ap, fmt);
3596  werase(owl_global_get_curs_msgwin(&g));
3597 
3598  vsnprintf(buff, 2048, fmt, ap);
3599  owl_function_debugmsg("makemsg: %s", buff);
3600  waddstr(owl_global_get_curs_msgwin(&g), buff); 
3601  wnoutrefresh(owl_global_get_curs_msgwin(&g));
3602  owl_global_set_needrefresh(&g);
3603  va_end(ap);
3604}
3605
3606/* get locations for everyone in .anyone.  If 'notify' is '1' then
3607 * send a pseudo login or logout message for everyone not in sync with
3608 * the global zephyr buddy list.  The list is updated regardless of
3609 * the status of 'notify'.
3610 */
3611void owl_function_zephyr_buddy_check(int notify)
3612{
3613#ifdef HAVE_LIBZEPHYR
3614  int i, j;
3615  owl_list anyone;
3616  owl_message *m;
3617  owl_zbuddylist *zbl;
3618  char *user;
3619  ZLocations_t location[200];
3620  int numlocs, ret;
3621
3622  zbl=owl_global_get_zephyr_buddylist(&g);
3623
3624  owl_list_create(&anyone);
3625  ret=owl_zephyr_get_anyone_list(&anyone, NULL);
3626
3627  j=owl_list_get_size(&anyone);
3628  for (i=0; i<j; i++) {
3629    user=owl_list_get_element(&anyone, i);
3630    ret=ZLocateUser(user, &numlocs, ZAUTH);
3631    if (ret!=ZERR_NONE) {
3632      owl_function_error("Error getting location for %s", user);
3633      continue;
3634    }
3635    numlocs=200;
3636    ret=ZGetLocations(location, &numlocs);
3637    if (ret==0) {
3638      if ((numlocs>0) && !owl_zbuddylist_contains_user(zbl, user)) {
3639        /* Send a PSEUDO LOGIN! */
3640        if (notify) {
3641          m=owl_malloc(sizeof(owl_message));
3642          owl_message_create_pseudo_zlogin(m, 0, user, location[0].host, location[0].time, location[0].tty);
3643          owl_global_messagequeue_addmsg(&g, m);
3644        }
3645        owl_zbuddylist_adduser(zbl, user);
3646        owl_function_debugmsg("owl_function_zephyr_buddy_check: login for %s ", user);
3647      } else if ((numlocs==0) && owl_zbuddylist_contains_user(zbl, user)) {
3648        /* I don't think this ever happens (if there are 0 locations we should get an error from
3649         * ZGetLocations)
3650         */
3651        owl_function_error("owl_function_zephyr_buddy_check: exceptional case logout for %s ",user);
3652      }
3653    } else if ((ret==ZERR_NOLOCATIONS) && owl_zbuddylist_contains_user(zbl, user)) {
3654      /* Send a PSEUDO LOGOUT! */
3655      if (notify) {
3656        m=owl_malloc(sizeof(owl_message));
3657        owl_message_create_pseudo_zlogin(m, 1, user, "", "", "");
3658        owl_global_messagequeue_addmsg(&g, m);
3659      }
3660      owl_zbuddylist_deluser(zbl, user);
3661      owl_function_debugmsg("owl_function_zephyr_buddy_check: logout for %s ",user);
3662    }
3663  }
3664
3665  owl_list_free_all(&anyone, owl_free);
3666#endif
3667}
3668
3669void owl_function_aimsearch_results(char *email, owl_list *namelist)
3670{
3671  owl_fmtext fm;
3672  int i, j;
3673
3674  owl_fmtext_init_null(&fm);
3675  owl_fmtext_append_normal(&fm, "AIM screennames associated with ");
3676  owl_fmtext_append_normal(&fm, email);
3677  owl_fmtext_append_normal(&fm, ":\n");
3678
3679  j=owl_list_get_size(namelist);
3680  for (i=0; i<j; i++) {
3681    owl_fmtext_append_normal(&fm, "  ");
3682    owl_fmtext_append_normal(&fm, owl_list_get_element(namelist, i));
3683    owl_fmtext_append_normal(&fm, "\n");
3684  }
3685
3686  owl_function_popless_fmtext(&fm);
3687  owl_fmtext_free(&fm);
3688}
3689
3690int owl_function_get_color_count()
3691{
3692     return COLORS;
3693}
Note: See TracBrowser for help on using the repository browser.