1 /***************************************************************************
3 openurl.library - universal URL display and browser launcher library
4 Copyright (C) 1998-2005 by Troels Walsted Hansen, et al.
5 Copyright (C) 2005-2013 by openurl.library Open Source Team
7 This library is free software; it has been placed in the public domain
8 and you can freely redistribute it and/or modify it. Please note, however,
9 that some components may be under the LGPL or GPL license.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 openurl.library project: http://sourceforge.net/projects/openurllib/
19 ***************************************************************************/
23 #include <proto/dos.h>
24 #include <proto/exec.h>
25 #include <proto/utility.h>
29 #if !defined(__amigaos4__)
30 #include <dos/dostags.h>
31 #include <exec/execbase.h>
36 /**************************************************************************/
38 #define FINDPORT_NUM 100 /* how many FindPort() to do while waiting */
39 #define FINDPORT_TIME 10 /* how many seconds to spread those FindPort() over */
40 #define FINDPORT_DTIME ((FINDPORT_TIME * TICKS_PER_SECOND)/FINDPORT_NUM)
48 #define PH_COUNT_BROWSER 2
49 #define PH_COUNT_MAILER 6
50 #define PH_COUNT_FTP 2
52 /**************************************************************************/
54 static STRPTR
expandPlaceHolders(STRPTR
template, struct placeHolder
*ph
, int num
)
61 for (p
= template; *p
; p
++)
63 for (i
= 0; i
<num
; i
++)
65 if ((*p
=='%') && (*(p
+1)== ph
[i
].ph_Char
))
66 length
+= strlen(ph
[i
].ph_String
);
72 if ((res
= allocArbitrateVecPooled(length
+1)) != NULL
)
74 for (p
= res
; *template; template++)
76 for (i
= 0; i
<num
; i
++)
77 if ((*template=='%') && (*(template+1)== ph
[i
].ph_Char
))
82 strcpy(p
,ph
[i
].ph_String
);
83 p
+= strlen(ph
[i
].ph_String
);
98 /**************************************************************************/
100 static BOOL
writeToFile(STRPTR fileName
, STRPTR str
)
107 if((fh
= Open(fileName
, MODE_NEWFILE
)))
109 LONG len
= strlen(str
);
111 if(Write(fh
, str
, len
)==len
)
121 /**************************************************************************/
123 static STRPTR
findRexxPort(struct List
*list
,STRPTR name
)
125 STRPTR portName
= NULL
;
129 /* find a rexx port, allowing a .<number> extension */
131 // avoid empty port names
132 if(name
!= NULL
&& name
[0] != '\0')
139 for(n
= list
->lh_Head
; n
->ln_Succ
; n
= n
->ln_Succ
)
141 if(n
->ln_Name
!= NULL
&& strncmp(n
->ln_Name
, name
, len
) == 0 &&
142 (n
->ln_Name
[len
] == '\0' || (n
->ln_Name
[len
] == '.' && isdigits(&n
->ln_Name
[len
+1]))))
144 portName
= n
->ln_Name
;
154 /**************************************************************************/
156 static STRPTR
waitForRexxPort(STRPTR port
)
163 /* (busy) wait for the port to appear */
165 // avoid empty port names
166 if(port
!= NULL
&& port
[0] != '\0')
168 for(i
= 0; i
<FINDPORT_NUM
; i
++)
173 rxport
= findRexxPort(&((struct ExecBase
*)SysBase
)->PortList
, port
);
182 if(SetSignal(0, 0) & SIGBREAKF_CTRL_C
)
188 Delay(FINDPORT_DTIME
);
196 /**************************************************************************/
198 static BOOL
sendRexxMsg(STRPTR rxport
, STRPTR rxcmd
)
201 struct Process
*proc
;
205 D(DBF_STARTUP
, "sending command '%s' to port '%s'", rxcmd
, rxport
);
207 #if defined(__MORPHOS__)
208 proc
= CreateNewProcTags(NP_Entry
, handler
,
209 NP_CodeType
, CODETYPE_PPC
,
210 NP_PPCStackSize
, 8192,
212 NP_Name
, "OpenURL - Handler",
215 NP_CloseInput
, FALSE
,
217 NP_CloseOutput
, FALSE
,
219 NP_CloseError
, FALSE
,
222 proc
= CreateNewProcTags(NP_Entry
, handler
,
224 NP_Name
, "OpenURL - Handler",
227 NP_CloseInput
, FALSE
,
229 NP_CloseOutput
, FALSE
,
231 NP_CloseError
, FALSE
,
237 struct MsgPort
*port
;
239 #if defined(__amigaos4__)
240 port
= AllocSysObjectTags(ASOT_PORT
, TAG_DONE
);
242 port
= CreateMsgPort();
247 struct startMsg
*smsg
;
249 #if defined(__amigaos4__)
250 smsg
= AllocSysObjectTags(ASOT_MESSAGE
, ASOMSG_Size
, sizeof(*smsg
),
251 ASOMSG_ReplyPort
, port
,
254 smsg
= allocArbitrateVecPooled(sizeof(*smsg
));
259 #if !defined(__amigaos4__)
260 INITMESSAGE(smsg
, port
, sizeof(*smsg
));
266 ObtainSemaphore(&OpenURLBase
->libSem
);
267 OpenURLBase
->rexx_use
++;
268 ReleaseSemaphore(&OpenURLBase
->libSem
);
270 PutMsg(&proc
->pr_MsgPort
, (struct Message
*)smsg
);
276 #if defined(__amigaos4__)
277 FreeSysObject(ASOT_MESSAGE
, smsg
);
279 freeArbitrateVecPooled(smsg
);
283 #if defined(__amigaos4__)
284 FreeSysObject(ASOT_PORT
, port
);
295 /****************************************************************************/
297 BOOL
sendToBrowser(STRPTR URL
, struct List
*portlist
, ULONG flags
, STRPTR pubScreenName
)
301 struct placeHolder ph
[PH_COUNT_BROWSER
];
302 struct URL_BrowserNode
*bn
;
306 /* set up the placeholder mapping */
308 ph
[0].ph_Char
= 'u'; ph
[0].ph_String
= URL
;
309 ph
[1].ph_Char
= 'p'; ph
[1].ph_String
= pubScreenName
? pubScreenName
: (STRPTR
)"Workbench";
311 /* try to find one of the browsers in the list */
313 for (bn
= (struct URL_BrowserNode
*)OpenURLBase
->prefs
->up_BrowserList
.mlh_Head
;
314 bn
->ubn_Node
.mln_Succ
;
315 bn
= (struct URL_BrowserNode
*)bn
->ubn_Node
.mln_Succ
)
319 if(isFlagSet(bn
->ubn_Flags
, UNF_DISABLED
))
321 if(bn
->ubn_Path
[0] == '\0')
324 port
= findRexxPort(portlist
,bn
->ubn_Port
);
328 /* send uniconify msg */
330 if (isFlagSet(flags
, SENDTOF_SHOW
) && *bn
->ubn_ShowCmd
)
331 sendRexxMsg(port
,bn
->ubn_ShowCmd
);
333 /* send screentofront command */
335 if (isFlagSet(flags
, SENDTOF_TOFRONT
) && *bn
->ubn_ToFrontCmd
)
336 sendRexxMsg(port
,bn
->ubn_ToFrontCmd
);
338 /* try sending openurl msg */
340 if (!(cmd
= expandPlaceHolders(isFlagSet(flags
, SENDTOF_NEWWINDOW
) ? bn
->ubn_OpenURLWCmd
: bn
->ubn_OpenURLCmd
,ph
,PH_COUNT_BROWSER
)))
343 if (!(res
= sendRexxMsg(port
,cmd
)))
345 freeArbitrateVecPooled(cmd
);
352 /* no running browser, launch a new one */
354 if (isFlagClear(flags
, SENDTOF_LAUNCH
))
357 for (bn
= (struct URL_BrowserNode
*)OpenURLBase
->prefs
->up_BrowserList
.mlh_Head
;
358 bn
->ubn_Node
.mln_Succ
;
359 bn
= (struct URL_BrowserNode
*)bn
->ubn_Node
.mln_Succ
)
367 if(isFlagSet(bn
->ubn_Flags
, UNF_DISABLED
))
369 if(bn
->ubn_Path
[0] == '\0')
372 /* compose commandline */
374 if (strstr(bn
->ubn_Path
,"%u")) startOnly
= TRUE
;
375 else startOnly
= FALSE
;
377 if (!(cmd
= expandPlaceHolders(bn
->ubn_Path
,ph
,PH_COUNT_BROWSER
)))
380 filePart
= FilePart(bn
->ubn_Path
);
388 lock
= Lock(bn
->ubn_Path
,ACCESS_READ
);
390 if (filePart
) *filePart
= c
;
392 /* start the browser */
394 error
= SystemTags(cmd
,SYS_Asynch
, TRUE
,
395 SYS_Input
, Open("NIL:",MODE_NEWFILE
),
398 lock
? NP_CurrentDir
: TAG_IGNORE
, lock
,
401 freeArbitrateVecPooled(cmd
);
406 if (lock
) UnLock(lock
);
414 /* send urlopen command */
416 if (!(cmd
= expandPlaceHolders(bn
->ubn_OpenURLCmd
,ph
,PH_COUNT_BROWSER
)))
419 /* wait for the port to appear */
421 if ((rxport
= waitForRexxPort(bn
->ubn_Port
)))
422 res
= sendRexxMsg(rxport
,cmd
);
435 freeArbitrateVecPooled(cmd
);
441 /**************************************************************************/
443 BOOL
sendToFTP(STRPTR URL
, struct List
*portlist
, ULONG flags
, STRPTR pubScreenName
)
447 struct placeHolder ph
[PH_COUNT_FTP
];
448 struct URL_FTPNode
*fn
;
452 /* set up the placeholder mapping */
454 ph
[0].ph_Char
= 'u'; /*ph[0].ph_String = URL;*/
455 ph
[1].ph_Char
= 'p'; ph
[1].ph_String
= pubScreenName
? pubScreenName
: (STRPTR
)"Workbench";
457 /* try to find one of the ftp client in the list */
459 for (fn
= (struct URL_FTPNode
*)OpenURLBase
->prefs
->up_FTPList
.mlh_Head
;
460 fn
->ufn_Node
.mln_Succ
;
461 fn
= (struct URL_FTPNode
*)fn
->ufn_Node
.mln_Succ
)
465 if(isFlagSet(fn
->ufn_Flags
, UNF_DISABLED
))
468 port
= findRexxPort(portlist
,fn
->ufn_Port
);
472 /* send uniconify msg */
474 if (isFlagSet(flags
, SENDTOF_SHOW
) && *fn
->ufn_ShowCmd
)
475 sendRexxMsg(port
,fn
->ufn_ShowCmd
);
477 /* send screentofront command */
479 if (isFlagSet(flags
, SENDTOF_TOFRONT
) && *fn
->ufn_ToFrontCmd
)
480 sendRexxMsg(port
,fn
->ufn_ToFrontCmd
);
482 /* try sending openurl msg */
484 if(isFlagSet(fn
->ufn_Flags
, UFNF_REMOVEFTP
) && !Strnicmp(URL
,"ftp://",6))
485 ph
[0].ph_String
= URL
+6;
487 ph
[0].ph_String
= URL
+6;
489 if (!(cmd
= expandPlaceHolders(isFlagSet(flags
, SENDTOF_NEWWINDOW
) ? fn
->ufn_OpenURLWCmd
: fn
->ufn_OpenURLCmd
,ph
,PH_COUNT_FTP
)))
492 if (!(res
= sendRexxMsg(port
,cmd
)))
494 freeArbitrateVecPooled(cmd
);
501 /* no running ftp client, launch a new one */
503 if (isFlagClear(flags
, SENDTOF_LAUNCH
))
506 for (fn
= (struct URL_FTPNode
*)OpenURLBase
->prefs
->up_FTPList
.mlh_Head
;
507 fn
->ufn_Node
.mln_Succ
;
508 fn
= (struct URL_FTPNode
*)fn
->ufn_Node
.mln_Succ
)
516 if(isFlagSet(fn
->ufn_Flags
, UNF_DISABLED
))
518 if(fn
->ufn_Path
[0] == '\0')
521 /* compose commandline */
523 if (strstr(fn
->ufn_Path
,"%u"))
528 if(isFlagSet(fn
->ufn_Flags
, UFNF_REMOVEFTP
) && !Strnicmp(URL
,"ftp://",6))
529 ph
[0].ph_String
= URL
+6;
531 ph
[0].ph_String
= URL
+6;
533 if (!(cmd
= expandPlaceHolders(fn
->ufn_Path
,ph
,PH_COUNT_FTP
)))
536 filePart
= FilePart(fn
->ufn_Path
);
544 lock
= Lock(fn
->ufn_Path
,ACCESS_READ
);
546 if (filePart
) *filePart
= c
;
548 /* start the ftp client */
550 error
= SystemTags(cmd
,SYS_Asynch
, TRUE
,
551 SYS_Input
, Open("NIL:",MODE_NEWFILE
),
554 lock
? NP_CurrentDir
: TAG_IGNORE
, lock
,
557 freeArbitrateVecPooled(cmd
);
562 if (lock
) UnLock(lock
);
570 /* send urlopen command */
572 if (!(cmd
= expandPlaceHolders(fn
->ufn_OpenURLCmd
,ph
,PH_COUNT_FTP
)))
575 /* wait for the port to appear */
577 if ((rxport
= waitForRexxPort(fn
->ufn_Port
)))
578 res
= sendRexxMsg(rxport
,cmd
);
591 freeArbitrateVecPooled(cmd
);
597 /**************************************************************************/
599 static WORD trans
[256];
601 BOOL
sendToMailer(STRPTR URL
, struct List
*portlist
, ULONG flags
, STRPTR pubScreenName
)
603 struct placeHolder ph
[PH_COUNT_MAILER
];
604 struct URL_MailerNode
*mn
;
605 STRPTR start
, end
, data
, address
= NULL
, subject
= NULL
, body
= NULL
,
608 BOOL res
= FALSE
, written
= FALSE
;
614 ObtainSemaphore(&OpenURLBase
->libSem
);
615 if(isFlagClear(OpenURLBase
->flags
, BASEFLG_Trans
))
617 for (len
= 0; len
<256; len
++) trans
[len
] = -1;
629 trans
['A'] = trans
['a'] = 10;
630 trans
['B'] = trans
['b'] = 11;
631 trans
['C'] = trans
['c'] = 12;
632 trans
['D'] = trans
['d'] = 13;
633 trans
['E'] = trans
['e'] = 14;
634 trans
['F'] = trans
['f'] = 15;
636 SET_FLAG(OpenURLBase
->flags
, BASEFLG_Trans
);
638 ReleaseSemaphore(&OpenURLBase
->libSem
);
640 /* parse the URL "mailto:user@host.domain?subject=Subject&body=Body" */
649 /* Use utility.library - Piru */
651 if (!Strnicmp(start
,"mailto:",7))
655 end
= strchr(start
+offset
,'?');
657 else if ((!Strnicmp(start
,"?subject=",9)) || (!Strnicmp(start
,"&subject=",9)))
661 end
= strchr(start
+offset
,'&');
663 else if ((!Strnicmp(start
,"?body=",6)) || (!Strnicmp(start
,"&body=",6)))
667 end
= strchr(start
+offset
,'&');
670 /* if we found some data && we even found it the first time ! */
675 if (end
) len
=end
-data
;
676 else len
=strlen(data
);
678 if(!(*tag
= allocArbitrateVecPooled(len
+1)))
681 strncpy(*tag
,data
,len
);
684 /* decode %XX sequences in urls */
688 if ((data
=strchr(data
,'%')) && (trans
[(int)data
[1]]!=-1) && (trans
[(int)data
[2]]!=-1))
690 *data
=(trans
[(int)data
[1]]<<4)|trans
[(int)data
[2]];
692 memmove(data
,data
+2,strlen(data
+2)+1);
701 snprintf(fileName
, sizeof(fileName
), "T:OpenURL-MailBody.%016lx", (IPTR
)FindTask(NULL
));
705 strlcpy(fileName
, "NIL:", sizeof(fileName
));
708 /* set up the placeholder mapping */
710 ph
[0].ph_Char
= 'a'; ph
[0].ph_String
= address
? address
: (STRPTR
)"";
711 ph
[1].ph_Char
= 's'; ph
[1].ph_String
= subject
? subject
: (STRPTR
)"";//URL;
712 ph
[2].ph_Char
= 'b'; ph
[2].ph_String
= body
? body
: (STRPTR
)"";
713 ph
[3].ph_Char
= 'f'; ph
[3].ph_String
= fileName
;
714 ph
[4].ph_Char
= 'u'; ph
[4].ph_String
= URL
;
715 ph
[5].ph_Char
= 'p'; ph
[5].ph_String
= pubScreenName
? pubScreenName
: (STRPTR
)"Workbench";
717 /* try to find one of the mailers in the list */
719 for (mn
= (struct URL_MailerNode
*)OpenURLBase
->prefs
->up_MailerList
.mlh_Head
;
720 mn
->umn_Node
.mln_Succ
;
721 mn
= (struct URL_MailerNode
*)mn
->umn_Node
.mln_Succ
)
725 if(isFlagSet(mn
->umn_Flags
, UNF_DISABLED
))
728 rxport
= findRexxPort(portlist
,mn
->umn_Port
);
732 /* send uniconify msg */
734 if (isFlagSet(flags
, SENDTOF_SHOW
) && *mn
->umn_ShowCmd
)
735 sendRexxMsg(rxport
,mn
->umn_ShowCmd
);
737 /* send screentofront command */
739 if (isFlagSet(flags
, SENDTOF_TOFRONT
) && *mn
->umn_ToFrontCmd
)
740 sendRexxMsg(rxport
,mn
->umn_ToFrontCmd
);
742 /* write to temp file */
744 if (!written
&& strstr(mn
->umn_WriteMailCmd
,"%f"))
745 written
= writeToFile(fileName
,body
);
747 /* try sending writemail msg */
749 if (!(cmd
= expandPlaceHolders(mn
->umn_WriteMailCmd
,ph
,PH_COUNT_MAILER
)))
752 /* now split each message at the ';' and send fragments */
756 while ((*end
) && (*end
!=';'))
759 /* skip data, which is enclosed in "" */
763 while ((*end
) && (*end
!='"')) end
++;
764 if (*end
=='"') end
++;
767 /* are there more commands */
773 if (!(res
= sendRexxMsg(rxport
,start
)))
775 /* send failed, try next mailer */
776 freeArbitrateVecPooled(cmd
);
781 /* cmd processed succesfully, return */
786 /* no running ftp client, launch a new one */
788 if (isFlagClear(flags
, SENDTOF_LAUNCH
))
791 for (mn
= (struct URL_MailerNode
*)OpenURLBase
->prefs
->up_MailerList
.mlh_Head
;
792 mn
->umn_Node
.mln_Succ
;
793 mn
= (struct URL_MailerNode
*)mn
->umn_Node
.mln_Succ
)
801 if(isFlagSet(mn
->umn_Flags
, UNF_DISABLED
))
803 if(mn
->umn_Path
[0] == '\0')
806 /* compose commandline */
808 if (strstr(mn
->umn_Path
,"%a"))
813 if (!written
&& strstr(mn
->umn_Path
,"%f"))
814 written
= writeToFile(fileName
,body
);
816 if (!(cmd
= expandPlaceHolders(mn
->umn_Path
,ph
,PH_COUNT_MAILER
)))
819 filePart
= FilePart(mn
->umn_Path
);
827 lock
= Lock(mn
->umn_Path
,ACCESS_READ
);
829 if (filePart
) *filePart
= c
;
831 /* start the mailer */
833 error
= SystemTags(cmd
,SYS_Asynch
, TRUE
,
834 SYS_Input
, Open("NIL:", MODE_NEWFILE
),
837 lock
? NP_CurrentDir
: TAG_IGNORE
, lock
,
840 freeArbitrateVecPooled(cmd
);
845 if (lock
) UnLock(lock
);
853 /* send write mail command */
855 if (!written
&& strstr(mn
->umn_WriteMailCmd
,"%f"))
856 /*written = */writeToFile(fileName
,body
);
858 if (!(cmd
= expandPlaceHolders(mn
->umn_WriteMailCmd
,ph
,PH_COUNT_MAILER
)))
861 /* wait for the port to appear */
863 if ((rxport
= waitForRexxPort(mn
->umn_Port
)))
868 while ((*end
) && (*end
!=';'))
871 /* skip data, which is enclosed in "" */
875 while ((*end
) && (*end
!='"')) end
++;
876 if (*end
=='"') end
++;
879 /* are there more commands */
885 if (!(res
= sendRexxMsg(rxport
,start
)))
887 /* send failed, try next mailer */
888 freeArbitrateVecPooled(cmd
);
904 if (cmd
) freeArbitrateVecPooled(cmd
);
905 if (body
) freeArbitrateVecPooled(body
);
906 if (subject
) freeArbitrateVecPooled(subject
);
907 if (address
) freeArbitrateVecPooled(address
);
913 /**************************************************************************/
915 BOOL
copyList(struct List
*dst
, struct List
*src
, ULONG size
)
918 struct Node
*n
, *new;
922 /* copy src list into dst, and return success */
924 for(n
= src
->lh_Head
; n
->ln_Succ
; n
= n
->ln_Succ
)
926 if((new = allocArbitrateVecPooled(size
)) == NULL
)
941 /**************************************************************************/
943 void freeList(struct List
*list
)
949 while((n
= RemHead(list
)) != NULL
)
950 freeArbitrateVecPooled(n
);
955 /**************************************************************************/
957 BOOL
isdigits(STRPTR str
)
970 else if(!isdigit(*str
))
980 #if !defined(HAVE_ALLOCVECPOOLED)
981 APTR
allocVecPooled(APTR pool
, ULONG size
)
987 size
+= sizeof(ULONG
);
988 if((mem
= AllocPooled(pool
, size
)))
996 /****************************************************************************/
998 #if !defined(HAVE_FREEVECPOOLED)
999 void freeVecPooled(APTR pool
,APTR mem
)
1003 FreePooled(pool
,(LONG
*)mem
- 1,*((LONG
*)mem
- 1));
1009 /****************************************************************************/
1011 APTR
reallocVecPooled(APTR pool
, APTR mem
, ULONG oldSize
, ULONG newSize
)
1017 if((newMem
= allocVecPooled(pool
, newSize
)) != NULL
)
1019 memcpy(newMem
, mem
, (oldSize
< newSize
) ? oldSize
: newSize
);
1021 freeVecPooled(pool
, mem
);
1028 /****************************************************************************/
1030 APTR
allocArbitrateVecPooled(ULONG size
)
1036 ObtainSemaphore(&OpenURLBase
->poolSem
);
1037 mem
= allocVecPooled(OpenURLBase
->pool
, size
);
1038 ReleaseSemaphore(&OpenURLBase
->poolSem
);
1044 /****************************************************************************/
1046 void freeArbitrateVecPooled(APTR mem
)
1050 ObtainSemaphore(&OpenURLBase
->poolSem
);
1051 freeVecPooled(OpenURLBase
->pool
, mem
);
1052 ReleaseSemaphore(&OpenURLBase
->poolSem
);
1057 /****************************************************************************/
1059 APTR
reallocArbitrateVecPooled(APTR mem
, ULONG oldSize
, ULONG newSize
)
1063 ObtainSemaphore(&OpenURLBase
->poolSem
);
1064 mem
= reallocVecPooled(OpenURLBase
->pool
, mem
, oldSize
, newSize
);
1065 ReleaseSemaphore(&OpenURLBase
->poolSem
);
1071 /****************************************************************************/