Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / openurl / library / utils.c
blob1aca5d3907d942502de29b7d8905ed789c27a23f
1 /*
2 ** openurl.library - universal URL display and browser
3 ** launcher library
4 **
5 ** Written by Troels Walsted Hansen <troels@thule.no>
6 ** Placed in the public domain.
7 **
8 ** Developed by:
9 ** - Alfonso Ranieri <alforan@tin.it>
10 ** - Stefan Kost <ensonic@sonicpulse.de>
12 ** Ported to OS4 by Alexandre Balaban <alexandre@balaban.name>
16 #include "lib.h"
17 #include <dos/dostags.h>
18 #include <exec/execbase.h>
20 /**************************************************************************/
22 #define FINDPORT_NUM 100 /* how many FindPort() to do while waiting */
23 #define FINDPORT_TIME 10 /* how many seconds to spread those FindPort() over */
24 #define FINDPORT_DTIME ((FINDPORT_TIME * TICKS_PER_SECOND)/FINDPORT_NUM)
26 struct placeHolder
28 UBYTE ph_Char;
29 UBYTE *ph_String;
32 #define PH_COUNT_BROWSER 2
33 #define PH_COUNT_MAILER 6
34 #define PH_COUNT_FTP 2
36 /**************************************************************************/
38 static UBYTE *
39 expandPlaceHolders(UBYTE *template,struct placeHolder *ph,int num)
41 UBYTE *p, *res;
42 int i, length = 0;
44 for (p = template; *p; p++)
46 for (i = 0; i<num; i++)
48 if ((*p=='%') && (*(p+1)== ph[i].ph_Char))
49 length += strlen(ph[i].ph_String);
52 length++;
55 if (!(res = allocVecPooled(length+1)))
56 return NULL;
58 for (p = res; *template; template++)
60 for (i = 0; i<num; i++)
61 if ((*template=='%') && (*(template+1)== ph[i].ph_Char))
62 break;
64 if (i<num)
66 strcpy(p,ph[i].ph_String);
67 p += strlen(ph[i].ph_String);
68 template++;
69 continue;
72 *p++ = *template;
75 *p = '\0';
77 return res;
80 /**************************************************************************/
82 static ULONG
83 writeToFile(UBYTE *fileName,UBYTE *str)
85 BPTR fh;
86 ULONG res = FALSE;
87 LONG len = strlen(str);
89 if ((fh = Open(fileName,MODE_NEWFILE)))
91 if (Write(fh,str,len)==len)
92 res = TRUE;
94 Close(fh);
97 return res;
100 /**************************************************************************/
102 static UBYTE *
103 findRexxPort(struct List *list,UBYTE *name)
105 struct Node *n;
106 ULONG len;
108 /* find a rexx port, allowing a .<number> extension */
110 len = strlen(name);
112 for (n = list->lh_Head; n->ln_Succ; n = n->ln_Succ)
114 if (n->ln_Name && !strncmp(n->ln_Name,name,len) &&
115 (n->ln_Name[len]=='\0' || (n->ln_Name[len]=='.' && isdigits(&n->ln_Name[len+1]))))
117 return n->ln_Name;
121 return NULL;
124 /**************************************************************************/
126 static UBYTE *
127 waitForRexxPort(UBYTE *port)
129 int i;
131 /* (busy) wait for the port to appear */
133 for (i = 0; i<FINDPORT_NUM; i++)
135 UBYTE *rxport;
137 Forbid();
138 rxport = findRexxPort(&SysBase->PortList,port);
139 Permit();
141 if (rxport) return(rxport);
143 if (SetSignal(0,0) & SIGBREAKF_CTRL_C)
144 return NULL;
146 Delay(FINDPORT_DTIME);
149 return NULL;
152 /**************************************************************************/
154 static ULONG
155 sendRexxMsg(UBYTE *rxport,UBYTE *rxcmd)
157 ULONG res = FALSE;
158 int sig;
160 if ((sig = AllocSignal(-1))>=0)
162 struct Process *proc;
163 struct TagItem attrs[] = {NP_Entry, (IPTR)handler,
164 #ifdef __MORPHOS__
165 NP_CodeType, CODETYPE_PPC,
166 NP_PPCStackSize, 8192,
167 #endif
168 NP_StackSize, 4196,
169 NP_Name, (IPTR)"OpenURL - Handler",
170 NP_CopyVars, FALSE,
171 NP_Input, (IPTR)NULL,
172 NP_CloseInput, FALSE,
173 NP_Output, (IPTR)NULL,
174 NP_CloseOutput, FALSE,
175 NP_Error, (IPTR)NULL,
176 NP_CloseError, FALSE,
177 TAG_DONE};
179 if ((proc = CreateNewProcTagList(attrs)))
181 struct MsgPort port;
182 struct startMsg smsg;
184 Forbid();
185 lib_use++;
186 Permit();
188 INITPORT(&port,sig);
190 memset(&smsg,0,sizeof(smsg));
191 INITMESSAGE(&smsg,&port,sizeof(smsg));
192 smsg.port = rxport;
193 smsg.cmd = rxcmd;
195 PutMsg(&proc->pr_MsgPort,(struct Message *)&smsg);
196 WaitPort(&port);
197 GetMsg(&port);
199 res = smsg.res;
202 FreeSignal(sig);
205 return res;
208 /****************************************************************************/
210 ULONG
211 sendToBrowser(UBYTE *URL,
212 struct List *portlist,
213 ULONG show,
214 ULONG toFront,
215 ULONG newWindow,
216 ULONG launch,
217 UBYTE *pubScreenName)
219 ULONG res = FALSE;
220 UBYTE *cmd = NULL;
221 struct placeHolder ph[PH_COUNT_BROWSER];
222 struct URL_BrowserNode *bn;
224 /* set up the placeholder mapping */
226 ph[0].ph_Char = 'u'; ph[0].ph_String = URL;
227 ph[1].ph_Char = 'p'; ph[1].ph_String = pubScreenName ? pubScreenName : (UBYTE *)"Workbench";
229 /* try to find one of the browsers in the list */
231 for (bn = (struct URL_BrowserNode *)lib_prefs->up_BrowserList.mlh_Head;
232 bn->ubn_Node.mln_Succ;
233 bn = (struct URL_BrowserNode *)bn->ubn_Node.mln_Succ)
235 UBYTE *port;
237 if (bn->ubn_Flags & UNF_DISABLED) continue;
239 port = findRexxPort(portlist,bn->ubn_Port);
241 if (port)
243 /* send uniconify msg */
245 if (show && *bn->ubn_ShowCmd)
246 sendRexxMsg(port,bn->ubn_ShowCmd);
248 /* send screentofront command */
250 if (toFront && *bn->ubn_ToFrontCmd)
251 sendRexxMsg(port,bn->ubn_ToFrontCmd);
253 /* try sending openurl msg */
255 if (!(cmd = expandPlaceHolders(newWindow ? bn->ubn_OpenURLWCmd : bn->ubn_OpenURLCmd,ph,PH_COUNT_BROWSER)))
256 goto done;
258 if (!(res = sendRexxMsg(port,cmd)))
260 freeVecPooled(cmd);
261 cmd = NULL;
263 else goto done;
267 /* no running browser, launch a new one */
269 if (!launch) goto done;
271 for (bn = (struct URL_BrowserNode *)lib_prefs->up_BrowserList.mlh_Head;
272 bn->ubn_Node.mln_Succ;
273 bn = (struct URL_BrowserNode *)bn->ubn_Node.mln_Succ)
275 ULONG startOnly;
276 UBYTE *filePart, c = '\0';
277 BPTR lock;
278 LONG error;
280 if (bn->ubn_Flags & UNF_DISABLED) continue;
281 if (!*bn->ubn_Path) continue;
283 /* compose commandline */
285 if (strstr(bn->ubn_Path,"%u")) startOnly = TRUE;
286 else startOnly = FALSE;
288 if (!(cmd = expandPlaceHolders(bn->ubn_Path,ph,PH_COUNT_BROWSER)))
289 goto done;
291 filePart = FilePart(bn->ubn_Path);
293 if (filePart)
295 c = *filePart;
296 *filePart = '\0';
299 lock = Lock(bn->ubn_Path,ACCESS_READ);
301 if (filePart) *filePart = c;
303 /* start the browser */
305 error = SystemTags(cmd,SYS_Asynch, TRUE,
306 SYS_Input, Open("NIL:",MODE_NEWFILE),
307 SYS_Output, NULL,
308 lock ? NP_CurrentDir : TAG_IGNORE, lock,
309 TAG_DONE);
311 freeVecPooled(cmd);
312 cmd = NULL;
314 if (error)
316 if (lock) UnLock(lock);
317 continue;
320 if (!startOnly)
322 UBYTE *rxport;
324 /* send urlopen command */
326 if (!(cmd = expandPlaceHolders(bn->ubn_OpenURLCmd,ph,PH_COUNT_BROWSER)))
327 goto done;
329 /* wait for the port to appear */
331 if ((rxport = waitForRexxPort(bn->ubn_Port)))
332 res = sendRexxMsg(rxport,cmd);
334 break;
336 else
338 res = TRUE;
339 break;
343 done:
344 if (cmd) freeVecPooled(cmd);
346 return res;
349 /**************************************************************************/
351 ULONG
352 sendToFTP(UBYTE *URL,
353 struct List *portlist,
354 ULONG show,
355 ULONG toFront,
356 ULONG newWindow,
357 ULONG launch,
358 UBYTE *pubScreenName)
360 ULONG res = FALSE;
361 UBYTE *cmd = NULL;
362 struct placeHolder ph[PH_COUNT_FTP];
363 struct URL_FTPNode *fn;
365 /* set up the placeholder mapping */
367 ph[0].ph_Char = 'u'; /*ph[0].ph_String = URL;*/
368 ph[1].ph_Char = 'p'; ph[1].ph_String = pubScreenName ? pubScreenName : (UBYTE *)"Workbench";
370 /* try to find one of the ftp client in the list */
372 for (fn = (struct URL_FTPNode *)lib_prefs->up_FTPList.mlh_Head;
373 fn->ufn_Node.mln_Succ;
374 fn = (struct URL_FTPNode *)fn->ufn_Node.mln_Succ)
376 UBYTE *port;
378 if (fn->ufn_Flags & UNF_DISABLED) continue;
380 port = findRexxPort(portlist,fn->ufn_Port);
382 if (port)
384 /* send uniconify msg */
386 if (show && *fn->ufn_ShowCmd)
387 sendRexxMsg(port,fn->ufn_ShowCmd);
389 /* send screentofront command */
391 if (toFront && *fn->ufn_ToFrontCmd)
392 sendRexxMsg(port,fn->ufn_ToFrontCmd);
394 /* try sending openurl msg */
396 if (fn->ufn_Flags & UFNF_REMOVEFTP && !Strnicmp(URL,"ftp://",6))
397 ph[0].ph_String = URL+6;
398 else ph[0].ph_String = URL+6;
400 if (!(cmd = expandPlaceHolders(newWindow ? fn->ufn_OpenURLWCmd : fn->ufn_OpenURLCmd,ph,PH_COUNT_FTP)))
401 goto done;
403 if (!(res = sendRexxMsg(port,cmd)))
405 freeVecPooled(cmd);
406 cmd = NULL;
408 else goto done;
412 /* no running ftp client, launch a new one */
414 if (!launch) goto done;
416 for (fn = (struct URL_FTPNode *)lib_prefs->up_FTPList.mlh_Head;
417 fn->ufn_Node.mln_Succ;
418 fn = (struct URL_FTPNode *)fn->ufn_Node.mln_Succ)
420 ULONG startOnly;
421 UBYTE *filePart, c = '\0';
422 BPTR lock;
423 LONG error;
425 if (fn->ufn_Flags & UNF_DISABLED) continue;
426 if (!*fn->ufn_Path) continue;
428 /* compose commandline */
430 if (strstr(fn->ufn_Path,"%u"))
431 startOnly = TRUE;
432 else
433 startOnly = FALSE;
435 if (fn->ufn_Flags & UFNF_REMOVEFTP && !Strnicmp(URL,"ftp://",6))
436 ph[0].ph_String = URL+6;
437 else ph[0].ph_String = URL+6;
439 if (!(cmd = expandPlaceHolders(fn->ufn_Path,ph,PH_COUNT_FTP)))
440 goto done;
442 filePart = FilePart(fn->ufn_Path);
444 if (filePart)
446 c = *filePart;
447 *filePart = '\0';
450 lock = Lock(fn->ufn_Path,ACCESS_READ);
452 if (filePart) *filePart = c;
454 /* start the ftp client */
456 error = SystemTags(cmd,SYS_Asynch, TRUE,
457 SYS_Input, Open("NIL:",MODE_NEWFILE),
458 SYS_Output, NULL,
459 lock ? NP_CurrentDir : TAG_IGNORE, lock,
460 TAG_DONE);
462 freeVecPooled(cmd);
463 cmd = NULL;
465 if (error)
467 if (lock) UnLock(lock);
468 continue;
471 if (!startOnly)
473 UBYTE *rxport;
475 /* send urlopen command */
477 if (!(cmd = expandPlaceHolders(fn->ufn_OpenURLCmd,ph,PH_COUNT_FTP)))
478 goto done;
480 /* wait for the port to appear */
482 if ((rxport = waitForRexxPort(fn->ufn_Port)))
483 res = sendRexxMsg(rxport,cmd);
485 break;
487 else
489 res = TRUE;
490 break;
494 done:
495 if (cmd) freeVecPooled(cmd);
496 return res;
499 /**************************************************************************/
501 static WORD trans[256];
503 ULONG
504 sendToMailer(UBYTE *URL,
505 struct List *portlist,
506 ULONG show,
507 ULONG toFront,
508 ULONG launch,
509 UBYTE *pubScreenName)
511 struct placeHolder ph[PH_COUNT_MAILER];
512 struct URL_MailerNode *mn;
513 UBYTE *start, *end, *data, *address = NULL, *subject = NULL, *body = NULL,
514 *cmd = NULL, **tag, fileName[32];
515 ULONG res = FALSE, written = FALSE;
516 UWORD offset, len;
518 /* setup trans */
519 ObtainSemaphore(&lib_sem);
520 if (!(lib_flags & BASEFLG_Trans))
522 for (len = 0; len<256; len++) trans[len] = -1;
524 trans['0'] = 0;
525 trans['1'] = 1;
526 trans['2'] = 2;
527 trans['3'] = 3;
528 trans['4'] = 4;
529 trans['5'] = 5;
530 trans['6'] = 6;
531 trans['7'] = 7;
532 trans['8'] = 8;
533 trans['9'] = 9;
534 trans['A'] = trans['a'] = 10;
535 trans['B'] = trans['b'] = 11;
536 trans['C'] = trans['c'] = 12;
537 trans['D'] = trans['d'] = 13;
538 trans['E'] = trans['e'] = 14;
539 trans['F'] = trans['f'] = 15;
541 lib_flags |= BASEFLG_Trans;
543 ReleaseSemaphore(&lib_sem);
545 /* parse the URL "mailto:user@host.domain?subject=Subject&body=Body" */
547 start = URL;
548 while (start)
550 tag = NULL;
551 end = NULL;
552 offset = 1;
554 /* Use utility.library - Piru */
556 if (!Strnicmp(start,"mailto:",7))
558 tag = &address;
559 offset = 7;
560 end = strchr(start+offset,'?');
562 else if ((!Strnicmp(start,"?subject=",9)) || (!Strnicmp(start,"&subject=",9)))
564 tag = &subject;
565 offset = 9;
566 end = strchr(start+offset,'&');
568 else if ((!Strnicmp(start,"?body=",6)) || (!Strnicmp(start,"&body=",6)))
570 tag = &body;
571 offset = 6;
572 end = strchr(start+offset,'&');
575 /* if we found some data && we even found it the first time ! */
576 if (tag && !*tag)
578 data=start+offset;
580 if (end) len=end-data;
581 else len=strlen(data);
583 if (!(*tag = allocVecPooled(len+1))) goto done;
585 strncpy(*tag,data,len);
586 *((*tag)+len)='\0';
588 /* decode %XX sequences in urls */
589 data=*tag;
590 while (data)
592 if ((data=strchr(data,'%')) && (trans[data[1]]!=-1) && (trans[data[2]]!=-1))
594 *data=(trans[data[1]]<<4)|trans[data[2]];
595 data++;
596 memmove(data,data+2,strlen(data+2)+1);
601 start = end;
604 if (body) msprintf(fileName,"T:OpenURL-MailBody.%08lx",(IPTR)FindTask(NULL));
605 else
607 written = TRUE;
608 strcpy(fileName,"NIL:");
611 /* set up the placeholder mapping */
613 ph[0].ph_Char = 'a'; ph[0].ph_String = address ? address : (UBYTE *)"";
614 ph[1].ph_Char = 's'; ph[1].ph_String = subject ? subject : (UBYTE *)"";//URL;
615 ph[2].ph_Char = 'b'; ph[2].ph_String = body ? body : (UBYTE *)"";
616 ph[3].ph_Char = 'f'; ph[3].ph_String = fileName;
617 ph[4].ph_Char = 'u'; ph[4].ph_String = URL;
618 ph[5].ph_Char = 'p'; ph[5].ph_String = pubScreenName ? pubScreenName : (UBYTE *)"Workbench";
620 /* try to find one of the mailers in the list */
622 for (mn = (struct URL_MailerNode *)lib_prefs->up_MailerList.mlh_Head;
623 mn->umn_Node.mln_Succ;
624 mn = (struct URL_MailerNode *)mn->umn_Node.mln_Succ)
626 UBYTE *rxport;
628 if (mn->umn_Flags & UNF_DISABLED) continue;
630 rxport = findRexxPort(portlist,mn->umn_Port);
632 if (rxport)
634 /* send uniconify msg */
636 if (show && *mn->umn_ShowCmd)
637 sendRexxMsg(rxport,mn->umn_ShowCmd);
639 /* send screentofront command */
641 if (toFront && *mn->umn_ToFrontCmd)
642 sendRexxMsg(rxport,mn->umn_ToFrontCmd);
644 /* write to temp file */
646 if (!written && strstr(mn->umn_WriteMailCmd,"%f"))
647 written = writeToFile(fileName,body);
649 /* try sending writemail msg */
651 if (!(cmd = expandPlaceHolders(mn->umn_WriteMailCmd,ph,PH_COUNT_MAILER)))
652 goto done;
654 /* now split each message at the ';' and send fragments */
655 start = end = cmd;
656 while (*start)
658 while ((*end) && (*end!=';'))
660 end++;
661 /* skip data, which is enclosed in "" */
662 if (*end=='"')
664 end++;
665 while ((*end) && (*end!='"')) end++;
666 if (*end=='"') end++;
669 /* are there more commands */
670 if (*end==';')
672 *end='\0';
673 end++;
676 if (!(res = sendRexxMsg(rxport,start)))
678 /* send failed, try next mailer */
679 freeVecPooled(cmd);
680 start = cmd = NULL;
682 else start=end;
684 /* cmd processed succesfully, return */
685 if (cmd) goto done;
689 /* no running ftp client, launch a new one */
691 if (!launch) goto done;
693 for (mn = (struct URL_MailerNode *)lib_prefs->up_MailerList.mlh_Head;
694 mn->umn_Node.mln_Succ;
695 mn = (struct URL_MailerNode *)mn->umn_Node.mln_Succ)
697 ULONG startOnly;
698 UBYTE *filePart, c = '\0';
699 BPTR lock;
700 LONG error;
702 if (mn->umn_Flags & UNF_DISABLED) continue;
703 if (!*mn->umn_Path) continue;
705 /* compose commandline */
707 if (strstr(mn->umn_Path,"%a"))
708 startOnly = TRUE;
709 else
710 startOnly = FALSE;
712 if (!written && strstr(mn->umn_Path,"%f"))
713 written = writeToFile(fileName,body);
715 if (!(cmd = expandPlaceHolders(mn->umn_Path,ph,PH_COUNT_MAILER)))
716 goto done;
718 filePart = FilePart(mn->umn_Path);
720 if (filePart)
722 c = *filePart;
723 *filePart = '\0';
726 lock = Lock(mn->umn_Path,ACCESS_READ);
728 if (filePart) *filePart = c;
730 /* start the mailer */
732 error = SystemTags(cmd,SYS_Asynch, TRUE,
733 SYS_Input, Open("NIL:", MODE_NEWFILE),
734 SYS_Output, NULL,
735 lock ? NP_CurrentDir : TAG_IGNORE, lock,
736 TAG_DONE);
738 freeVecPooled(cmd);
739 cmd = NULL;
741 if (error)
743 if (lock) UnLock(lock);
744 continue;
747 if (!startOnly)
749 UBYTE *rxport;
751 /* send write mail command */
753 if (!written && strstr(mn->umn_WriteMailCmd,"%f"))
754 /*written = */writeToFile(fileName,body);
756 if (!(cmd = expandPlaceHolders(mn->umn_WriteMailCmd,ph,PH_COUNT_MAILER)))
757 goto done;
759 /* wait for the port to appear */
761 if ((rxport = waitForRexxPort(mn->umn_Port)))
763 start = end = cmd;
764 while (*start)
766 while ((*end) && (*end!=';'))
768 end++;
769 /* skip data, which is enclosed in "" */
770 if (*end=='"')
772 end++;
773 while ((*end) && (*end!='"')) end++;
774 if (*end=='"') end++;
777 /* are there more commands */
778 if (*end==';')
780 *end='\0';
781 end++;
783 if (!(res = sendRexxMsg(rxport,start)))
785 /* send failed, try next mailer */
786 freeVecPooled(cmd);
787 start = cmd = NULL;
789 else start=end;
792 break;
794 else
796 res = TRUE;
797 break;
801 done:
802 if (cmd) freeVecPooled(cmd);
803 if (body) freeVecPooled(body);
804 if (subject) freeVecPooled(subject);
805 if (address) freeVecPooled(address);
807 return res;
810 /**************************************************************************/
812 ULONG
813 copyList(struct List *dst,struct List *src,ULONG size)
815 struct Node *n, *new;
817 /* copy src list into dst, and return success */
819 for (n = src->lh_Head; n->ln_Succ; n = n->ln_Succ)
821 if (!(new = allocPooled(size)))
823 freeList(dst,size);
824 return FALSE;
827 CopyMem(n,new,size);
828 AddTail(dst,new);
831 return TRUE;
834 /**************************************************************************/
836 void
837 freeList(struct List *list,ULONG size)
839 struct Node *n;
841 while ((n = RemHead(list))) freePooled(n,size);
844 /**************************************************************************/
846 ULONG
847 isdigits(UBYTE *str)
849 for (;;)
851 if (!*str) return(TRUE);
852 else if (!isdigit(*str)) return(FALSE);
854 str++;
858 /**************************************************************************/
860 #if !defined(__MORPHOS__) && !defined(__amigaos4__) && !defined(__AROS__)
861 static UWORD fmtfunc[] = { 0x16c0, 0x4e75 };
862 void STDARGS
863 msprintf(UBYTE *buf,UBYTE *fmt,...)
865 RawDoFmt(fmt,&fmt+1,(APTR)fmtfunc,buf);
867 #elif defined(__amigaos4__)
868 #include <stdarg.h>
869 void VARARGS68K
870 msprintf(UBYTE *buf,UBYTE *fmt,...)
872 va_list va;
873 va_startlinear(va,fmt);
874 RawDoFmt(fmt, va_getlinearva(va,CONST APTR), (void (*)(void)) 0, buf);
875 va_end(va);
878 #endif
880 /**************************************************************************/
882 APTR
883 allocPooled(ULONG size)
885 ULONG *mem;
887 ObtainSemaphore(&lib_memSem);
888 mem = AllocPooled(lib_pool,size);
889 ReleaseSemaphore(&lib_memSem);
891 return mem;
894 /****************************************************************************/
896 void
897 freePooled(APTR mem,ULONG size)
899 ObtainSemaphore(&lib_memSem);
900 FreePooled(lib_pool,mem,size);
901 ReleaseSemaphore(&lib_memSem);
904 /****************************************************************************/
906 APTR
907 allocVecPooled(ULONG size)
909 ULONG *mem;
911 ObtainSemaphore(&lib_memSem);
912 if ((mem = AllocPooled(lib_pool,size = size+sizeof(ULONG)))) *mem++ = size;
913 ReleaseSemaphore(&lib_memSem);
915 return mem;
918 /****************************************************************************/
920 void
921 freeVecPooled(APTR mem)
923 ObtainSemaphore(&lib_memSem);
924 FreePooled(lib_pool,(ULONG *)mem-1,*((ULONG *)mem-1));
925 ReleaseSemaphore(&lib_memSem);
928 /****************************************************************************/