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