1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ Implementation of url.h.
3  *
4  * Copyright (c) 2014 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5  * SPDX-License-Identifier: ISC
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #undef su_FILE
20 #define su_FILE url
21 #define mx_SOURCE
22 
23 #ifndef mx_HAVE_AMALGAMATION
24 # include "mx/nail.h"
25 #endif
26 
27 #include <su/cs.h>
28 #include <su/mem.h>
29 
30 #ifdef mx_HAVE_NET
31 # include <su/icodec.h>
32 #endif
33 
34 #include "mx/cmd.h"
35 #include "mx/cred-auth.h"
36 #include "mx/cred-netrc.h"
37 #include "mx/file-streams.h"
38 #include "mx/ui-str.h"
39 
40 #include "mx/url.h"
41 /* TODO fake */
42 #include "su/code-in.h"
43 
44 /* Find the last @ before a slash
45  * TODO Casts off the const but this is ok here; obsolete function! */
46 #ifdef mx_HAVE_NET /* temporary (we'll have file://..) */
47 static char *a_url_last_at_before_slash(char const *cp);
48 #endif
49 
50 #ifdef mx_HAVE_NET
51 static char *
a_url_last_at_before_slash(char const * cp)52 a_url_last_at_before_slash(char const *cp){
53    char const *xcp;
54    char c;
55    NYD2_IN;
56 
57    for(xcp = cp; (c = *xcp) != '\0'; ++xcp)
58       if(c == '/')
59          break;
60    while(xcp > cp && *--xcp != '@')
61       ;
62    if(*xcp != '@')
63       xcp = NIL;
64 
65    NYD2_OU;
66    return UNCONST(char*,xcp);
67 }
68 #endif
69 
70 char *
71 (mx_url_xenc)(char const *cp, boole ispath  su_DBG_LOC_ARGS_DECL){
72    char *n, *np, c1;
73    NYD2_IN;
74 
75    /* C99 */{
76       uz i;
77 
78       i = su_cs_len(cp);
79       if(i >= UZ_MAX / 3){
80          n = NIL;
81          goto jleave;
82       }
83       i *= 3;
84       ++i;
85       np = n = su_MEM_BAG_SELF_AUTO_ALLOC_LOCOR(i, su_DBG_LOC_ARGS_ORUSE);
86    }
87 
88    for(; (c1 = *cp) != '\0'; ++cp){
89       /* (RFC 1738) RFC 3986, 2.3 Unreserved Characters:
90        *    ALPHA / DIGIT / "-" / "." / "_" / "~"
91        * However add special is[file]path mode for file-system friendliness */
92       if(su_cs_is_alnum(c1) || c1 == '_')
93          *np++ = c1;
94       else if(!ispath) {
95          if(c1 != '-' && c1 != '.' && c1 != '~')
96             goto jesc;
97          *np++ = c1;
98       }else if(PCMP(np, >, n) && (*cp == '-' || *cp == '.')) /* XXX imap */
99          *np++ = c1;
100       else{
101 jesc:
102          np[0] = '%';
103          n_c_to_hex_base16(np + 1, c1);
104          np += 3;
105       }
106    }
107    *np = '\0';
108 
109 jleave:
110    NYD2_OU;
111    return n;
112 }
113 
114 char *
115 (mx_url_xdec)(char const *cp  su_DBG_LOC_ARGS_DECL){
116    char *n, *np;
117    s32 c;
118    NYD2_IN;
119 
120    np = n = su_MEM_BAG_SELF_AUTO_ALLOC_LOCOR(su_cs_len(cp) +1,
121          su_DBG_LOC_ARGS_ORUSE);
122 
123    while((c = S(uc,*cp++)) != '\0'){
124       if(c == '%' && cp[0] != '\0' && cp[1] != '\0'){
125          s32 o = c;
126          if(LIKELY((c = n_c_from_hex_base16(cp)) >= '\0'))
127             cp += 2;
128          else
129             c = o;
130       }
131       *np++ = S(char,c);
132    }
133    *np = '\0';
134 
135    NYD2_OU;
136    return n;
137 }
138 
139 int
c_urlcodec(void * vp)140 c_urlcodec(void *vp){
141    boole ispath;
142    uz alen;
143    char const **argv, *varname, *varres, *act, *cp;
144    NYD_IN;
145 
146    argv = vp;
147    varname = (n_pstate & n_PS_ARGMOD_VPUT) ? *argv++ : NIL;
148 
149    act = *argv;
150    for(cp = act; *cp != '\0' && !su_cs_is_space(*cp); ++cp)
151       ;
152    if((ispath = (*act == 'p'))){
153       if(!su_cs_cmp_case_n(++act, "ath", 3))
154          act += 3;
155    }
156    if(act >= cp)
157       goto jesynopsis;
158    alen = P2UZ(cp - act);
159    if(*cp != '\0')
160       ++cp;
161 
162    n_pstate_err_no = su_ERR_NONE;
163 
164    if(su_cs_starts_with_case_n("encode", act, alen))
165       varres = mx_url_xenc(cp, ispath);
166    else if(su_cs_starts_with_case_n("decode", act, alen))
167       varres = mx_url_xdec(cp);
168    else
169       goto jesynopsis;
170 
171    if(varres == NIL){
172       n_pstate_err_no = su_ERR_CANCELED;
173       varres = cp;
174       vp = NIL;
175    }
176 
177    if(varname != NIL){
178       if(!n_var_vset(varname, (up)varres)){
179          n_pstate_err_no = su_ERR_NOTSUP;
180          cp = NIL;
181       }
182    }else{
183       struct str in, out;
184 
185       in.l = su_cs_len(in.s = n_UNCONST(varres));
186       makeprint(&in, &out);
187       if(fprintf(n_stdout, "%s\n", out.s) < 0){
188          n_pstate_err_no = su_err_no();
189          vp = NIL;
190       }
191       n_free(out.s);
192    }
193 
194 jleave:
195    NYD_OU;
196    return (vp != NIL ? 0 : 1);
197 jesynopsis:
198    mx_cmd_print_synopsis(mx_cmd_firstfit("urlcodec"), NIL);
199    n_pstate_err_no = su_ERR_INVAL;
200    vp = NIL;
201    goto jleave;
202 }
203 
204 char *
mx_url_mailto_to_address(char const * mailtop)205 mx_url_mailto_to_address(char const *mailtop){ /* TODO hack RFC6068 factory? */
206    uz i;
207    char *rv;
208    char const *mailtop_orig;
209    NYD_IN;
210 
211    if(!su_cs_starts_with(mailtop_orig = mailtop, "mailto:")){
212       rv = NIL;
213       goto jleave;
214    }
215    mailtop += sizeof("mailto:") -1;
216 
217    /* TODO This is all intermediate, and for now just enough to understand
218     * TODO a little bit of a little more advanced List-Post: headers. */
219    /* Strip any hfield additions, keep only to addr-spec's */
220    if((rv = su_cs_find_c(mailtop, '?')) != NIL)
221       rv = savestrbuf(mailtop, i = P2UZ(rv - mailtop));
222    else
223       rv = savestrbuf(mailtop, i = su_cs_len(mailtop));
224 
225    i = su_cs_len(rv);
226 
227    /* Simply perform percent-decoding if there is a percent % */
228    if(su_mem_find(rv, '%', i) != NIL){
229       char *rv_base;
230       boole err;
231 
232       for(err = FAL0, mailtop = rv_base = rv; i > 0;){
233          char c;
234 
235          if((c = *mailtop++) == '%'){
236             s32 cc;
237 
238             if(i < 3 || (cc = n_c_from_hex_base16(mailtop)) < 0){
239                if(!err && (err = TRU1, n_poption & n_PO_D_V))
240                   n_err(_("Invalid RFC 6068 'mailto' URL: %s\n"),
241                      n_shexp_quote_cp(mailtop_orig, FAL0));
242                goto jhex_putc;
243             }
244             *rv++ = S(char,cc);
245             mailtop += 2;
246             i -= 3;
247          }else{
248 jhex_putc:
249             *rv++ = c;
250             --i;
251          }
252       }
253       *rv = '\0';
254       rv = rv_base;
255    }
256 
257 jleave:
258    NYD_OU;
259    return rv;
260 }
261 
262 char const *
mx_url_servbyname(char const * proto,u16 * port_or_nil,boole * issnd_or_nil)263 mx_url_servbyname(char const *proto, u16 *port_or_nil, boole *issnd_or_nil){
264    static struct{
265       char const name[14];
266       char const port[7];
267       boole issnd;
268       u16 portno;
269    } const tbl[] = {
270       {"smtp", "25", TRU1, 25},
271       {"smtps", "465", TRU1, 465},
272       {"submission", "587", TRU1, 587},
273       {"submissions", "465", TRU1, 465},
274       {"pop3", "110", FAL0, 110},
275       {"pop3s", "995", FAL0, 995},
276       {"imap", "143", FAL0, 143},
277       {"imaps", "993", FAL0, 993},
278       {"file", "", TRU1, 0},
279       {"test", "", TRU1, U16_MAX}
280    };
281    char const *rv;
282    uz l, i;
283    NYD2_IN;
284 
285    for(rv = proto;; ++rv)
286       if(*rv == '\0'){
287          rv = NIL;
288          goto jleave;
289       }else if(*rv == ':')
290          break;
291    l = P2UZ(rv - proto);
292 
293    for(rv = NIL, i = 0; i < NELEM(tbl); ++i)
294       if(!su_cs_cmp_case_n(tbl[i].name, proto, l)){
295          rv = tbl[i].port;
296          if(port_or_nil != NIL)
297             *port_or_nil = tbl[i].portno;
298          if(issnd_or_nil != NIL)
299             *issnd_or_nil = tbl[i].issnd;
300          break;
301       }
302 
303 jleave:
304    NYD2_OU;
305    return rv;
306 }
307 
308 #ifdef mx_HAVE_NET /* Note: not indented for that -- later: file:// etc.! */
309 boole
mx_url_parse(struct mx_url * urlp,enum cproto cproto,char const * data)310 mx_url_parse(struct mx_url *urlp, enum cproto cproto, char const *data){
311    /* TODO mx_url_parse() is a terrible mess; all the substrings should be
312     * TODO accessors of an object instead */
313 #if defined mx_HAVE_SMTP && defined mx_HAVE_POP3 && defined mx_HAVE_IMAP
314 # define a_ALLPROTO
315 #endif
316 #if defined mx_HAVE_SMTP || defined mx_HAVE_POP3 || defined mx_HAVE_IMAP || \
317       defined mx_HAVE_TLS
318 # define a_ANYPROTO
319    char *cp, *x;
320 #endif
321    boole rv;
322    NYD_IN;
323    UNUSED(data);
324 
325    su_mem_set(urlp, 0, sizeof *urlp);
326    urlp->url_input = data;
327    urlp->url_cproto = cproto;
328 
329    rv = FAL0;
330 
331 #ifdef mx_HAVE_TLS
332 # define a_OUCH 0
333 #else
334 # define a_OUCH 1
335 #endif
336 
337    /* Network protocol */
338 #define a_PROTOX(X,Y,Z) \
339    urlp->url_portno = Y;\
340    su_mem_copy(urlp->url_proto, X "://\0", sizeof(X "://\0"));\
341    urlp->url_proto[sizeof(X) -1] = '\0';\
342    urlp->url_proto_len = sizeof(X) -1;\
343    if(!a_OUCH){ Z; }
344 #define a_PRIVPROTOX(X,Y,Z) \
345    do{ a_PROTOX(X, Y, Z); }while(0)
346 
347 #define a__IF(T,X,Y,Z)  \
348    if(!su_cs_cmp_case_n(data, X "://", sizeof(X "://") -1)){\
349       if(a_OUCH && T)\
350          goto jeproto;\
351       a_PROTOX(X, Y, Z);\
352       data += sizeof(X "://") -1;\
353       goto juser;\
354    }
355 #define a_IF(X,Y) a__IF(0, X, Y, (void)0)
356 #define a_IFS(X,Y) a__IF(1, X, Y, urlp->url_flags |= mx_URL_TLS_REQUIRED)
357 #define a_IFs(X,Y) a__IF(0, X, Y, urlp->url_flags |= mx_URL_TLS_OPTIONAL)
358 
359    switch(cproto){
360    case CPROTO_NONE:
361       if(su_cs_find(data, "://") != NIL)
362          goto jeproto;
363       a_PROTOX("none", 0, (void)0);
364       break;
365    case CPROTO_CERTINFO:
366       /* The special `tls' certificate info protocol
367        * We do allow all protos here, for later getaddrinfo() usage! */
368 #ifdef mx_HAVE_TLS
369       if((cp = su_cs_find(data, "://")) == NIL)
370          a_PRIVPROTOX("https", 443, urlp->url_flags |= mx_URL_TLS_REQUIRED);
371       else{
372          uz i;
373 
374          if((i = P2UZ(&cp[sizeof("://") -1] - data)) + 2 >=
375                sizeof(urlp->url_proto))
376             goto jeproto;
377          su_mem_copy(urlp->url_proto, data, i);
378          data += i;
379          i -= sizeof("://") -1;
380          urlp->url_proto[i] = '\0';\
381          urlp->url_proto_len = i;
382          urlp->url_flags |= mx_URL_TLS_REQUIRED;
383       }
384       break;
385 #else
386       goto jeproto;
387 #endif
388    case CPROTO_CCRED:
389       /* The special S/MIME etc. credential lookup TODO TLS client cert! */
390 #ifdef mx_HAVE_TLS
391       a_PRIVPROTOX("ccred", 0, (void)0);
392       break;
393 #else
394       goto jeproto;
395 #endif
396    case CPROTO_SOCKS:
397       a_IF("socks5", 1080);
398       a_IF("socks", 1080);
399       a_PROTOX("socks", 1080, (void)0);
400       break;
401    case CPROTO_SMTP:
402 #ifdef mx_HAVE_SMTP
403       a_IFS("smtps", 465)
404       a_IFs("smtp", 25)
405       a_IFs("submission", 587)
406       a_IFS("submissions", 465)
407       a_PROTOX("smtp", 25, urlp->url_flags |= mx_URL_TLS_OPTIONAL);
408       break;
409 #else
410       goto jeproto;
411 #endif
412    case CPROTO_POP3:
413 #ifdef mx_HAVE_POP3
414       a_IFS("pop3s", 995)
415       a_IFs("pop3", 110)
416       a_PROTOX("pop3", 110, urlp->url_flags |= mx_URL_TLS_OPTIONAL);
417       break;
418 #else
419       goto jeproto;
420 #endif
421    case CPROTO_IMAP:
422 #ifdef mx_HAVE_IMAP
423       a_IFS("imaps", 993)
424       a_IFs("imap", 143)
425       a_PROTOX("imap", 143, urlp->url_flags |= mx_URL_TLS_OPTIONAL);
426       break;
427 #else
428       goto jeproto;
429 #endif
430    }
431 
432 #undef a_OUCH
433 #undef a_PRIVPROTOX
434 #undef a_PROTOX
435 #undef a__IF
436 #undef a_IF
437 #undef a_IFS
438 #undef a_IFs
439 
440    if(su_cs_find(data, "://") != NIL){
441 jeproto:
442       n_err(_("URL proto:// invalid (protocol or TLS support missing?): %s\n"),
443          urlp->url_input);
444       goto jleave;
445    }
446 #ifdef a_ANYPROTO
447 
448    /* User and password, I */
449 juser:
450    if((cp = a_url_last_at_before_slash(data)) != NIL){
451       uz l;
452       char const *urlpe, *d;
453       char *ub;
454 
455       l = P2UZ(cp - data);
456       ub = n_lofi_alloc(l +1);
457       d = data;
458       data = &cp[1];
459 
460       /* And also have a password? */
461       if((cp = su_mem_find(d, ':', l)) != NIL){
462          uz i = P2UZ(cp - d);
463 
464          l -= i + 1;
465          su_mem_copy(ub, cp + 1, l);
466          ub[l] = '\0';
467 
468          if((urlp->url_pass.s = mx_url_xdec(ub)) == NIL)
469             goto jurlp_err;
470          urlp->url_pass.l = su_cs_len(urlp->url_pass.s);
471          if((urlpe = mx_url_xenc(urlp->url_pass.s, FAL0)) == NIL)
472             goto jurlp_err;
473          if(su_cs_cmp(ub, urlpe))
474             goto jurlp_err;
475          l = i;
476       }
477 
478       su_mem_copy(ub, d, l);
479       ub[l] = '\0';
480       if((urlp->url_user.s = mx_url_xdec(ub)) == NIL)
481          goto jurlp_err;
482       if((urlp->url_user.l = su_cs_len(urlp->url_user.s)) > 0){
483          urlp->url_flags |= mx_URL_HAD_USER;
484 
485          if((urlp->url_user_enc.s = mx_url_xenc(urlp->url_user.s, FAL0)
486                ) == NIL)
487             goto jurlp_err;
488          urlp->url_user_enc.l = su_cs_len(urlp->url_user_enc.s);
489 
490          if(urlp->url_user_enc.l != l ||
491                su_mem_cmp(urlp->url_user_enc.s, ub, l)){
492 jurlp_err:
493             n_err(_("Not URL percent encoded (use `urlcodec enc'): %s\n"),
494                ub);
495             d = NIL;
496          }
497       }else
498          urlp->url_user.s = NIL;
499 
500       n_lofi_free(ub);
501       if(d == NIL)
502          goto jleave;
503    }
504 
505    /* Servername and port -- and possible path suffix */
506    if((cp = su_cs_find_c(data, ':')) != NIL){ /* TODO URL: use IPAddress! */
507       urlp->url_port = x = savestr(x = &cp[1]);
508       if((x = su_cs_find_c(x, '/')) != NIL){
509          *x = '\0';
510          while(*++x == '/')
511             ;
512       }
513 
514       if((su_idec_u16_cp(&urlp->url_portno, urlp->url_port, 10, NIL
515                ) & (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
516             ) != su_IDEC_STATE_CONSUMED || urlp->url_portno == 0){
517          n_err(_("URL with invalid port number: %s\n"), urlp->url_input);
518          goto jleave;
519       }
520    }else{
521       if((x = su_cs_find_c(data, '/')) != NIL){
522          data = savestrbuf(data, P2UZ(x - data));
523          while(*++x == '/')
524             ;
525       }
526       cp = n_UNCONST(data + su_cs_len(data));
527    }
528 
529    /* A (non-empty) path may only occur with IMAP */
530    if(x != NIL && *x != '\0'){
531       /* Take care not to count adjacent solidus for real, on either end */
532       char *x2;
533       uz i;
534       boole trailsol;
535 
536       for(trailsol = FAL0, x2 = savestrbuf(x, i = su_cs_len(x)); i > 0;
537             trailsol = TRU1, --i)
538          if(x2[i - 1] != '/')
539             break;
540       x2[i] = '\0';
541 
542       if(i > 0){
543          if(cproto != CPROTO_IMAP){
544             n_err(_("URL protocol doesn't support paths: \"%s\"\n"),
545                urlp->url_input);
546             goto jleave;
547          }
548 # ifdef mx_HAVE_IMAP
549          if(trailsol){
550             urlp->url_path.s = n_autorec_alloc(i + sizeof("/INBOX"));
551             su_mem_copy(urlp->url_path.s, x, i);
552             su_mem_copy(&urlp->url_path.s[i], "/INBOX", sizeof("/INBOX"));
553             urlp->url_path.l = (i += sizeof("/INBOX") -1);
554          }else
555 # endif
556             urlp->url_path.l = i, urlp->url_path.s = x2;
557       }
558    }
559 # ifdef mx_HAVE_IMAP
560    if(cproto == CPROTO_IMAP && urlp->url_path.s == NIL)
561       urlp->url_path.s = savestrbuf("INBOX",
562             urlp->url_path.l = sizeof("INBOX") -1);
563 # endif
564 
565    urlp->url_host.s = savestrbuf(data, urlp->url_host.l = P2UZ(cp - data));
566    /* C99 */{
567       uz i;
568 
569       for(cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
570          *cp = su_cs_to_lower(*cp);
571    }
572 # ifdef mx_HAVE_IDNA
573    if(!ok_blook(idna_disable)){
574       struct n_string idna;
575 
576       if(!n_idna_to_ascii(n_string_creat_auto(&idna), urlp->url_host.s,
577                urlp->url_host.l)){
578          n_err(_("URL host fails IDNA conversion: %s\n"), urlp->url_input);
579          goto jleave;
580       }
581       urlp->url_host.s = n_string_cp(&idna);
582       urlp->url_host.l = idna.s_len;
583    }
584 # endif /* mx_HAVE_IDNA */
585 
586    /* .url_h_p: HOST:PORT */
587    /* C99 */{
588       uz upl, i;
589       struct str *s = &urlp->url_h_p;
590 
591       upl = (urlp->url_port == NIL) ? 0 : 1u + su_cs_len(urlp->url_port);
592       s->s = n_autorec_alloc(urlp->url_host.l + upl +1);
593       su_mem_copy(s->s, urlp->url_host.s, i = urlp->url_host.l);
594       if(upl > 0){
595          s->s[i++] = ':';
596          su_mem_copy(&s->s[i], urlp->url_port, --upl);
597          i += upl;
598       }
599       s->s[s->l = i] = '\0';
600    }
601 
602    /* User, II
603     * If there was no user in the URL, do we have *user-HOST* or *user*?
604     * This only for protocols which want to have additional information */
605    if(cproto != CPROTO_NONE && cproto != CPROTO_CERTINFO &&
606          !(urlp->url_flags & mx_URL_HAD_USER)){
607       /* *user* guaranteed non-empty */
608       if((urlp->url_user.s = xok_vlook(user, urlp, OXM_PLAIN | OXM_H_P)
609             ) == NIL){
610          /* No, check whether .netrc lookup is desired */
611 # ifdef mx_HAVE_NETRC
612          struct mx_netrc_entry nrce;
613 
614          if(ok_vlook(v15_compat) == NIL ||
615                !xok_blook(netrc_lookup, urlp, OXM_PLAIN | OXM_H_P) ||
616                !mx_netrc_lookup(&nrce, urlp) ||
617                (urlp->url_user.s = UNCONST(char*,nrce.nrce_login)) == NIL)
618 # endif
619             urlp->url_user.s = UNCONST(char*,ok_vlook(LOGNAME));
620       }
621 
622       urlp->url_user.l = su_cs_len(urlp->url_user.s);
623       urlp->url_user.s = savestrbuf(urlp->url_user.s, urlp->url_user.l);
624       if((urlp->url_user_enc.s = mx_url_xenc(urlp->url_user.s, FAL0)) == NIL){
625          n_err(_("Cannot URL encode %s\n"), urlp->url_user.s);
626          goto jleave;
627       }
628       urlp->url_user_enc.l = su_cs_len(urlp->url_user_enc.s);
629    }
630 
631    /* And then there are a lot of prebuild string combinations TODO do lazy */
632 
633    /* .url_u_h: .url_user@.url_host
634     * For SMTP we apply ridiculously complicated *v15-compat* plus
635     * *smtp-hostname* / *hostname* dependent rules */
636    /* C99 */{
637       struct str h, *s;
638       uz i;
639 
640       if(cproto == CPROTO_SMTP && ok_vlook(v15_compat) != NIL &&
641             (cp = ok_vlook(smtp_hostname)) != NIL){
642          if(*cp == '\0')
643             cp = n_nodename(TRU1);
644          h.s = savestrbuf(cp, h.l = su_cs_len(cp));
645       }else
646          h = urlp->url_host;
647 
648       s = &urlp->url_u_h;
649       i = urlp->url_user.l;
650 
651       s->s = n_autorec_alloc(i + 1 + h.l +1);
652       if(i > 0){
653          su_mem_copy(s->s, urlp->url_user.s, i);
654          s->s[i++] = '@';
655       }
656       su_mem_copy(s->s + i, h.s, h.l +1);
657       i += h.l;
658       s->l = i;
659    }
660 
661    /* .url_u_h_p: .url_user@.url_host[:.url_port] */
662    /* C99 */{
663       struct str *s = &urlp->url_u_h_p;
664       uz i = urlp->url_user.l;
665 
666       s->s = n_autorec_alloc(i + 1 + urlp->url_h_p.l +1);
667       if(i > 0){
668          su_mem_copy(s->s, urlp->url_user.s, i);
669          s->s[i++] = '@';
670       }
671       su_mem_copy(s->s + i, urlp->url_h_p.s, urlp->url_h_p.l +1);
672       i += urlp->url_h_p.l;
673       s->l = i;
674    }
675 
676    /* .url_eu_h_p: .url_user_enc@.url_host[:.url_port] */
677    /* C99 */{
678       struct str *s = &urlp->url_eu_h_p;
679       uz i = urlp->url_user_enc.l;
680 
681       s->s = n_autorec_alloc(i + 1 + urlp->url_h_p.l +1);
682       if(i > 0){
683          su_mem_copy(s->s, urlp->url_user_enc.s, i);
684          s->s[i++] = '@';
685       }
686       su_mem_copy(s->s + i, urlp->url_h_p.s, urlp->url_h_p.l +1);
687       i += urlp->url_h_p.l;
688       s->l = i;
689    }
690 
691    /* .url_p_u_h_p: .url_proto://.url_u_h_p */
692    /* C99 */{
693       uz i;
694       char *ud;
695 
696       ud = n_autorec_alloc((i = urlp->url_proto_len + sizeof("://") -1 +
697             urlp->url_u_h_p.l) +1);
698       urlp->url_proto[urlp->url_proto_len] = ':';
699       su_mem_copy(su_cs_pcopy(ud, urlp->url_proto), urlp->url_u_h_p.s,
700          urlp->url_u_h_p.l +1);
701       urlp->url_proto[urlp->url_proto_len] = '\0';
702 
703       urlp->url_p_u_h_p = ud;
704    }
705 
706    /* .url_p_eu_h_p, .url_p_eu_h_p_p: .url_proto://.url_eu_h_p[/.url_path] */
707    /* C99 */{
708       uz i;
709       char *ud;
710 
711       ud = n_autorec_alloc((i = urlp->url_proto_len + sizeof("://") -1 +
712             urlp->url_eu_h_p.l) + 1 + urlp->url_path.l +1);
713       urlp->url_proto[urlp->url_proto_len] = ':';
714       su_mem_copy(su_cs_pcopy(ud, urlp->url_proto), urlp->url_eu_h_p.s,
715          urlp->url_eu_h_p.l +1);
716       urlp->url_proto[urlp->url_proto_len] = '\0';
717 
718       if(urlp->url_path.l == 0)
719          urlp->url_p_eu_h_p = urlp->url_p_eu_h_p_p = ud;
720       else{
721          urlp->url_p_eu_h_p = savestrbuf(ud, i);
722          urlp->url_p_eu_h_p_p = ud;
723          ud += i;
724          *ud++ = '/';
725          su_mem_copy(ud, urlp->url_path.s, urlp->url_path.l +1);
726       }
727    }
728 
729    rv = TRU1;
730 #endif /* a_ANYPROTO */
731 jleave:
732    NYD_OU;
733    return rv;
734 #undef a_ANYPROTO
735 #undef a_ALLPROTO
736 }
737 #endif /* mx_HAVE_NET */
738 
739 #include "su/code-ou.h"
740 /* s-it-mode */
741