Windows.Oberon.NetSystem.Mod 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. (* modified to obtain correct host name (*alm 11/11/2004*) *)
  3. MODULE NetSystem IN Oberon; (** portable *) (* pjm *)
  4. (* A Portable Oberon Interface to the Internet Protocols. *)
  5. IMPORT SYSTEM, Modules IN A2, IP IN A2, UDP IN A2, TCP IN A2, DNS IN A2,
  6. Kernel, Input, Texts, Oberon, Strings, Fonts;
  7. CONST
  8. CloseTimeout = 5000; (* ms to wait for Close to finish *)
  9. anyport* = 0; (** any port value *)
  10. (** result values *)
  11. done* = 0; (** everything went ok *)
  12. error* = 1; (** failure occured *)
  13. (** return values of procedure State *)
  14. closed* = 0; (** connection is closed (neither sending nor receiving) *)
  15. listening* = 1; (** passive connection is listening for a request *)
  16. in* = 2; (** receiving only *)
  17. out* = 3; (** sending only *)
  18. inout* = 4; (** sending and receiving is possible *)
  19. waitCon* = 5; (** still waiting to be connected *)
  20. errorCon* = 6; (** connecting failed *)
  21. CR = 0DX; LF = 0AX;
  22. TYPE
  23. Connection* = POINTER TO ConnectionDesc; (** handle for TCP connections *)
  24. ConnectionDesc* = RECORD
  25. res*: INTEGER; (** result of last operation on a connection (error indication) *)
  26. pcb: TCP.Connection
  27. END;
  28. IPAdr* = IP.Adr; (** IP address in network byte order! *)
  29. Socket* = POINTER TO SocketDesc; (** handle for UDP "connections" *)
  30. SocketDesc* = RECORD
  31. res*: INTEGER; (** result of last operation on a connection (error indication) *)
  32. pcb: UDP.Socket
  33. END;
  34. Password = POINTER TO PasswordDesc;
  35. PasswordDesc = RECORD
  36. service, user, host, passwd: ARRAY 64 OF CHAR;
  37. next: Password
  38. END;
  39. Bytes = ARRAY MAX(LONGINT) OF CHAR;
  40. VAR
  41. int*: IP.Interface;
  42. anyIP*: IPAdr; (** "NIL" ip-number *)
  43. allIP*: IPAdr; (** broadcast ip-number *)
  44. hostIP*: IPAdr; (** main ip-number of local machine *)
  45. hostName*: ARRAY 64 OF CHAR; (** main name of local machine *)
  46. crlf: ARRAY 2 OF CHAR;
  47. started: BOOLEAN;
  48. passwords: Password;
  49. w: Texts.Writer;
  50. state: ARRAY TCP.NumStates OF INTEGER;
  51. (** -- Adressing/Naming section. *)
  52. (** Convert a dotted IP address string (e.g. "1.2.3.4") to an IPAdr value. *)
  53. PROCEDURE ToHost* (num: ARRAY OF CHAR; VAR adr: IPAdr; VAR done: BOOLEAN);
  54. BEGIN
  55. adr := IP.StrToAdr(num);
  56. done := ~IP.IsNilAdr(adr);
  57. END ToHost;
  58. (** Convert an IPAdr value to a dotted IP address string. *)
  59. PROCEDURE ToNum*(adr: IPAdr; VAR num: ARRAY OF CHAR);
  60. BEGIN
  61. IP.AdrToStr(adr, num)
  62. END ToNum;
  63. (** GetIP delivers the ip-number of a named host. If a symbolic name is given, it will be resolved by use of domain name servers. *)
  64. PROCEDURE GetIP*(name: ARRAY OF CHAR; VAR adr: IPAdr);
  65. VAR res: LONGINT;
  66. BEGIN
  67. adr := IP.StrToAdr(name);
  68. IF IP.IsNilAdr(adr) THEN
  69. IF started THEN
  70. DNS.HostByName(name, adr, res);
  71. IF res # 0 THEN adr := IP.NilAdr END
  72. END
  73. END
  74. END GetIP;
  75. (** GetName is the reverse of GetIP. Given an ip-number, it delivers the name of a host. *)
  76. PROCEDURE GetName* (IP: IPAdr; VAR name: ARRAY OF CHAR);
  77. VAR res: LONGINT;
  78. BEGIN
  79. IF started THEN
  80. DNS.HostByNumber(IP, name, res)
  81. ELSE
  82. res := -1
  83. END;
  84. IF res # DNS.Ok THEN name[0] := 0X END
  85. END GetName;
  86. (** -- TCP section. *)
  87. (** OpenConnection opens a connection. locPort, remPort, remIP are contained in the quadrupel <locIP, remIP, locPort, remPort> which determines a connection uniquely. As locIP is always the current machine, it is omitted. If remPort is equal to anyport or remIP is equal to anyIP, a passive connection will be opened. After execution, C is a brand new connection. res indicates any error. *)
  88. PROCEDURE OpenConnection* (VAR C: Connection; locPort: INTEGER; remIP: IPAdr; remPort: INTEGER; VAR res: INTEGER);
  89. BEGIN
  90. AsyncOpenConnection(C, locPort, remIP, remPort, res);
  91. IF res = done THEN
  92. LOOP
  93. CASE State(C) OF
  94. closed, errorCon:
  95. res := error; C.res := res; EXIT
  96. |listening, in, out, inout:
  97. EXIT
  98. |waitCon:
  99. (* skip *)
  100. END
  101. END
  102. END
  103. END OpenConnection;
  104. (** Like OpenConnection, but this procedure may return immediately and delay the actual opening of the connection. In this case State() should be checked to wait for the connection status to change from waitCon. *)
  105. PROCEDURE AsyncOpenConnection*(VAR C: Connection; locPort: INTEGER; remIP: IPAdr; remPort:INTEGER; VAR res: INTEGER);
  106. VAR err: LONGINT;
  107. BEGIN
  108. NEW(C); NEW(C.pcb);
  109. C.pcb.Open(LONG(locPort) MOD 10000H, remIP, LONG(remPort) MOD 10000H, err);
  110. IF err = 0 THEN
  111. (*Kernel.WriteString("pcb="); Kernel.WriteHex(SYSTEM.VAL(LONGINT, C.pcb), 8); Kernel.WriteLn;*)
  112. Kernel.RegisterObject(C, Cleanup, FALSE);
  113. res := done
  114. ELSE
  115. C.pcb := NIL;
  116. res := error
  117. END;
  118. C.res := res
  119. END AsyncOpenConnection;
  120. (** Close the connection. Connection can not be used for send operations afterwards. *)
  121. PROCEDURE CloseConnection* (C: Connection);
  122. VAR err: LONGINT;
  123. BEGIN
  124. IF C.pcb # NIL THEN
  125. C.pcb.Close();
  126. C.pcb.AwaitState(TCP.ClosedStates, {}, CloseTimeout, err);
  127. IF err # 0 THEN C.res := error END;
  128. C.pcb := NIL
  129. END
  130. END CloseConnection;
  131. (** Discard the connection. Connection can not be used afterwards. *)
  132. PROCEDURE DiscardConnection* (C: Connection); (** non-portable *)
  133. VAR err: LONGINT;
  134. BEGIN
  135. IF C.pcb # NIL THEN
  136. C.pcb.Discard();
  137. C.pcb.AwaitState(TCP.ClosedStates, {}, CloseTimeout, err);
  138. IF err # 0 THEN C.res := error END;
  139. C.pcb := NIL
  140. END
  141. END DiscardConnection;
  142. PROCEDURE Cleanup(c: ANY);
  143. (*VAR s: ARRAY 20 OF CHAR;*)
  144. BEGIN
  145. WITH c: Connection DO
  146. IF c.pcb # NIL THEN
  147. (*IF Trace THEN
  148. KernelLog.String("NetSystem: Cleanup ");
  149. ToNum(SYSTEM.VAL(IPAdr, c.port.rip), s); KernelLog.String(s);
  150. KernelLog.Char(":"); KernelLog.Int(c.port.rport, 1);
  151. KernelLog.Ln
  152. END;*)
  153. CloseConnection(c); c.pcb := NIL
  154. END
  155. END
  156. END Cleanup;
  157. (** Indicates whether there exists a remote machine which wants to connect to the local one. This Procedure is only useful on passive connections. For active connections (State(C) # listen), it always delivers FALSE. *)
  158. PROCEDURE Requested* (C: Connection): BOOLEAN;
  159. BEGIN
  160. RETURN (C.pcb # NIL) & C.pcb.Requested()
  161. END Requested;
  162. (** Procedure accepts a new waiting, active connection (newC) on a passive one (State(C) = listen). If no connection is waiting, accept blocks until there is one or an error occurs. If C is not a passive connection, Accept does nothing but res is set to Done. *)
  163. PROCEDURE Accept* (C: Connection; VAR newC: Connection; VAR res: INTEGER);
  164. VAR client: TCP.Connection; err: LONGINT;
  165. BEGIN
  166. C.pcb.Accept(client, err);
  167. IF err = 0 THEN
  168. NEW(newC); newC.pcb := client;
  169. Kernel.RegisterObject(newC, Cleanup, FALSE);
  170. newC.res := done; res := done
  171. ELSE
  172. newC := NIL; res := error
  173. END
  174. END Accept;
  175. (** Procedure returns the state of a connection (see constant section). Even if a connection is closed, data can still be available in the local buffer. *)
  176. PROCEDURE State* (C: Connection): INTEGER;
  177. BEGIN
  178. IF C.pcb # NIL THEN RETURN state[C.pcb.State()] ELSE RETURN closed END
  179. END State;
  180. (** Returns the number of bytes which may be read without blocking. *)
  181. PROCEDURE Available* (C: Connection): LONGINT;
  182. BEGIN
  183. RETURN C.pcb.Available()
  184. END Available;
  185. (** Blocking read a single byte. *)
  186. PROCEDURE Read* (C: Connection; VAR ch: CHAR);
  187. VAR len, res: LONGINT; buf: ARRAY 1 OF CHAR;
  188. BEGIN
  189. C.pcb.Receive(buf, 0, 1, 1, len, res);
  190. IF (res = 0) & (len = 1) THEN ch := buf[0] ELSE C.res := error; ch := 0X END
  191. END Read;
  192. (** Blocking read len bytes of data (beginning at pos in buf) to buf. *)
  193. PROCEDURE ReadBytes* (C: Connection; pos, len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE);
  194. VAR res, read: LONGINT;
  195. BEGIN
  196. ASSERT(pos+len <= LEN(buf)); (* index check *)
  197. C.pcb.Receive(SYSTEM.VAL(Bytes, buf), pos, len, len, read, res);
  198. IF (res = 0) & (len = read) THEN (* skip *) ELSE C.res := error END
  199. END ReadBytes;
  200. (** Blocking read two bytes in network byte ordering. *)
  201. PROCEDURE ReadInt* (C: Connection; VAR x: INTEGER);
  202. VAR buf: ARRAY 2 OF CHAR; res, len: LONGINT;
  203. BEGIN
  204. C.pcb.Receive(buf, 0, 2, 2, len, res);
  205. IF (res = 0) & (len = 2) THEN
  206. x := ORD(buf[0])*100H + ORD(buf[1])
  207. ELSE
  208. x := 0; C.res := error
  209. END
  210. END ReadInt;
  211. (** Blocking read four bytes in network byte ordering. *)
  212. PROCEDURE ReadLInt* (C: Connection; VAR x: LONGINT);
  213. VAR buf: ARRAY 4 OF CHAR; res, len: LONGINT;
  214. BEGIN
  215. C.pcb.Receive(buf, 0, 4, 4, len, res);
  216. IF (res = 0) & (len = 4) THEN
  217. x := ORD(buf[0])*1000000H + ORD(buf[1])*10000H + ORD(buf[2])*100H + ORD(buf[3])
  218. ELSE
  219. x := 0; C.res := error
  220. END
  221. END ReadLInt;
  222. (** Blocking read a string terminated by ( [CR]LF | 0X ). *)
  223. PROCEDURE ReadString* (C: Connection; VAR s: ARRAY OF CHAR);
  224. VAR i: LONGINT; ch: CHAR;
  225. BEGIN
  226. i := 0;
  227. LOOP
  228. Read(C, ch);
  229. IF (ch = LF) OR (ch = 0X) THEN EXIT END;
  230. s[i] := ch; INC(i)
  231. END;
  232. IF (ch = LF) & (i > 0) & (s[i-1] = CR) THEN s[i-1] := 0X ELSE s[i] := 0X END
  233. END ReadString;
  234. (** Blocking write a single byte to C. *)
  235. PROCEDURE Write* (C: Connection; ch: CHAR);
  236. VAR res: LONGINT; buf: ARRAY 1 OF CHAR;
  237. BEGIN
  238. buf[0] := ch;
  239. C.pcb.Send(buf, 0, 1, FALSE, res);
  240. IF res = 0 THEN (* skip *) ELSE C.res := error END
  241. END Write;
  242. (** Blocking write len bytes of data (beginning at pos in buf) to C. *)
  243. PROCEDURE WriteBytes* (C: Connection; pos, len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE);
  244. VAR res: LONGINT;
  245. BEGIN
  246. ASSERT(pos+len <= LEN(buf));
  247. C.pcb.Send(SYSTEM.VAL(Bytes, buf), pos, len, FALSE, res);
  248. IF res = 0 THEN (* skip *) ELSE C.res := error END
  249. END WriteBytes;
  250. (** Blocking write two bytes in network byte ordering to C. *)
  251. PROCEDURE WriteInt* (C: Connection; x: INTEGER);
  252. VAR res: LONGINT; buf: ARRAY 2 OF CHAR;
  253. BEGIN
  254. buf[0] := CHR(x DIV 100H MOD 100H); buf[1] := CHR(x MOD 100H);
  255. C.pcb.Send(buf, 0, 2, FALSE, res);
  256. IF res = 0 THEN (* skip *) ELSE C.res := error END
  257. END WriteInt;
  258. (** Blocking write four bytes in network byte ordering to C. *)
  259. PROCEDURE WriteLInt* (C: Connection; x: LONGINT);
  260. VAR res: LONGINT; buf: ARRAY 4 OF CHAR;
  261. BEGIN
  262. buf[0] := CHR(x DIV 1000000H MOD 100H); buf[1] := CHR(x DIV 10000H MOD 100H);
  263. buf[2] := CHR(x DIV 100H MOD 100H); buf[3] := CHR(x MOD 100H);
  264. C.pcb.Send(buf, 0, 4, FALSE, res);
  265. IF res = 0 THEN (* skip *) ELSE C.res := error END
  266. END WriteLInt;
  267. (** Blocking write a string without "0X" and terminated by "CRLF" to C. *)
  268. PROCEDURE WriteString* (C: Connection; s: ARRAY OF CHAR);
  269. VAR i, res: LONGINT;
  270. BEGIN
  271. i := 0; WHILE s[i] # 0X DO INC(i) END;
  272. C.pcb.Send(s, 0, i, FALSE, res);
  273. IF res = 0 THEN
  274. C.pcb.Send(crlf, 0, 2, FALSE, res);
  275. IF res = 0 THEN (* skip *) ELSE C.res := error END
  276. ELSE
  277. C.res := error
  278. END
  279. END WriteString;
  280. (** Procedure delivers the ip-number and port number of a connection's remote partner. *)
  281. PROCEDURE GetPartner* (C: Connection; VAR remIP: IPAdr; VAR remPort: INTEGER);
  282. BEGIN
  283. IF C.pcb # NIL THEN
  284. remIP := C.pcb.fip; remPort := SHORT(C.pcb.fport)
  285. ELSE
  286. remIP := anyIP; remPort := anyport
  287. END
  288. END GetPartner;
  289. (** -- UDP section. *)
  290. (* Datagram oriented communication *)
  291. (** Opens a socket which is dedicated to datagram services. locPort is registered to receive datagrams from any port and any host. *)
  292. PROCEDURE OpenSocket* (VAR S: Socket; locPort: INTEGER; VAR res: INTEGER);
  293. VAR err: LONGINT;
  294. BEGIN
  295. NEW(S); NEW(S.pcb, LONG(locPort) MOD 10000H, err);
  296. IF err = 0 THEN res := done ELSE S.pcb := NIL; res := error END;
  297. S.res := res
  298. END OpenSocket;
  299. (** Closes the socket. You can not receive datagrams anymore. *)
  300. PROCEDURE CloseSocket* (S: Socket);
  301. BEGIN
  302. S.pcb.Close;
  303. S.pcb := NIL; S.res := done
  304. END CloseSocket;
  305. (** Sends len bytes of data (beginning at pos in buf) to the host specified by remIP and remPort. *)
  306. PROCEDURE SendDG* (S: Socket; remIP: IPAdr; remPort: INTEGER; pos, len: LONGINT; VAR buf: ARRAY OF SYSTEM.BYTE);
  307. VAR res: LONGINT;
  308. BEGIN
  309. ASSERT(pos+len <= LEN(buf)); (* index check *)
  310. S.pcb.Send(remIP, LONG(remPort) MOD 10000H, SYSTEM.VAL(Bytes, buf), pos, len, res);
  311. IF res = 0 THEN S.res := done ELSE S.res := error END
  312. END SendDG;
  313. (** Stores an entire datagram in buf beginning at pos. On success (S.res = done), remIP and remPort indicate the sender, len indicates the length of valid data. *)
  314. PROCEDURE ReceiveDG*(S: Socket; VAR remIP: IPAdr; VAR remPort: INTEGER; pos: LONGINT; VAR len: LONGINT;
  315. VAR buf: ARRAY OF SYSTEM.BYTE);
  316. VAR res, fport: LONGINT;
  317. BEGIN
  318. ASSERT(pos+len <= LEN(buf)); (* index check *)
  319. S.pcb.Receive(SYSTEM.VAL(Bytes, buf), pos, len, 0, remIP, fport, len, res);
  320. remPort := SHORT(fport);
  321. IF res = 0 THEN S.res := done ELSE S.res := error; len := -1 END
  322. END ReceiveDG;
  323. (** Returns the size of the first available datagram on the socket. *)
  324. PROCEDURE AvailableDG* (S: Socket): LONGINT;
  325. BEGIN
  326. RETURN 0
  327. END AvailableDG;
  328. (* Conversions *)
  329. (** Write 2 bytes in network byte ordering to buf[pos]. *)
  330. PROCEDURE PutInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; x: INTEGER);
  331. BEGIN
  332. buf[pos] := CHR(x DIV 100H MOD 100H);
  333. buf[pos+1] := CHR(x MOD 100H)
  334. END PutInt;
  335. (** Write 4 bytes in network byte ordering to buf[pos]. *)
  336. PROCEDURE PutLInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; x: LONGINT);
  337. BEGIN
  338. buf[pos] := CHR(x DIV 1000000H MOD 100H);
  339. buf[pos+1] := CHR(x DIV 10000H MOD 100H);
  340. buf[pos+2] := CHR(x DIV 100H MOD 100H);
  341. buf[pos+3] := CHR(x MOD 100H)
  342. END PutLInt;
  343. (** Read 2 bytes in network byte ordering from buf[pos]. *)
  344. PROCEDURE GetInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; VAR x: INTEGER);
  345. BEGIN
  346. x := ORD(buf[pos])*100H + ORD(buf[pos+1])
  347. END GetInt;
  348. (** Read 4 bytes in network byte ordering from buf[pos]. *)
  349. PROCEDURE GetLInt* (VAR buf: ARRAY OF SYSTEM.BYTE; pos: INTEGER; VAR x: LONGINT);
  350. BEGIN
  351. x := ORD(buf[pos])*1000000H + ORD(buf[pos+1])*10000H + ORD(buf[pos+2])*100H + ORD(buf[pos+3])
  352. END GetLInt;
  353. (** -- Passwords section. *)
  354. PROCEDURE WriteURL(VAR service, user, host: ARRAY OF CHAR);
  355. BEGIN
  356. Texts.WriteString(w, "NetSystem.SetUser "); Texts.WriteString(w, service);
  357. Texts.Write(w, ":"); Texts.WriteString(w, user); Texts.Write(w, "@");
  358. Texts.WriteString(w, host); Texts.WriteString(w, " ~"); Texts.WriteLn(w)
  359. END WriteURL;
  360. (** Retrieve the password for user using service on host. Parameters service, host and user must be specified.
  361. Parameter user is in/out. If empty, it returns the first (user,password) pair found, otherwise it returns the
  362. specified user's password. *)
  363. PROCEDURE GetPassword*(service, host: ARRAY OF CHAR; VAR user, password: ARRAY OF CHAR);
  364. VAR pass: Password; r: Texts.Reader; ch: CHAR;
  365. BEGIN
  366. Strings.Lower(service, service); Strings.Lower(host, host);
  367. pass := passwords;
  368. WHILE (pass # NIL) & ~((pass.service = service) & (pass.host = host) & ((user = "") OR (pass.user = user))) DO
  369. pass := pass.next
  370. END;
  371. IF pass # NIL THEN
  372. COPY(pass.user, user); COPY(pass.passwd, password)
  373. ELSE
  374. IF (service # "") & (user # "") THEN
  375. IF Oberon.Log.len > 0 THEN
  376. Texts.OpenReader(r, Oberon.Log, Oberon.Log.len-1);
  377. Texts.Read(r, ch);
  378. IF ch # CHR(13) THEN Texts.WriteLn(w) END
  379. END;
  380. WriteURL(service, user, host); Texts.Append(Oberon.Log, w.buf)
  381. END;
  382. COPY("", user); COPY("", password)
  383. END
  384. END GetPassword;
  385. (** Remove password for user using service on host. *)
  386. PROCEDURE DelPassword*(service, user, host: ARRAY OF CHAR);
  387. VAR ppass, pass: Password;
  388. BEGIN
  389. Strings.Lower(service, service); Strings.Lower(host, host);
  390. ppass := NIL; pass := passwords;
  391. WHILE (pass # NIL) & ((pass.service # service) & (pass.host # host) & (pass.user # user)) DO
  392. ppass := pass; pass := pass.next
  393. END;
  394. IF pass # NIL THEN
  395. IF ppass # NIL THEN
  396. ppass.next := pass.next
  397. ELSE
  398. passwords := pass.next
  399. END
  400. END
  401. END DelPassword;
  402. (** Command NetSystem.SetUser { service ":" ["//"] [ user [ ":" password ] "@" ] host [ "/" ] } "~" <enter password>
  403. If password is not specified in-line, prompts for the password for the (service, host, user) triple.
  404. The (service, host, user, password) 4-tuple is stored in memory for retrieval with GetPassword.
  405. Multiple identical passwords may be set with one command. *)
  406. PROCEDURE SetUser*;
  407. VAR
  408. R: Texts.Reader;
  409. service, usr, host, pwd, entered: ARRAY 64 OF CHAR;
  410. ok, verbose: BOOLEAN;
  411. ch: CHAR;
  412. pass: Password;
  413. PROCEDURE Next(VAR str: ARRAY OF CHAR);
  414. VAR i: LONGINT;
  415. BEGIN
  416. Texts.Read(R, ch);
  417. WHILE ~R.eot & ((ch <= " ") OR (ch = ":") OR (ch = "@") OR (ch = "/") OR ~(R.lib IS Fonts.Font)) DO
  418. Texts.Read(R, ch)
  419. END;
  420. i := 0;
  421. WHILE ~R.eot & (ch > " ") & (ch # ":") & (ch # "@") & (ch # "/") & (ch # "~") & (R.lib IS Fonts.Font) DO
  422. str[i] := ch; INC(i); Texts.Read(R, ch)
  423. END;
  424. str[i] := 0X
  425. END Next;
  426. PROCEDURE InputStr(prompt: ARRAY OF CHAR; show: BOOLEAN; VAR str: ARRAY OF CHAR);
  427. VAR i: LONGINT; ch: CHAR;
  428. BEGIN
  429. Texts.SetColor(w, 1); Texts.WriteString(w, prompt); Texts.SetColor(w, 15);
  430. Texts.Append(Oberon.Log, w.buf);
  431. Input.Read (ch); i := 0;
  432. WHILE (ch # 0DX) & (ch # 1AX) DO
  433. IF ch = 7FX THEN
  434. IF i > 0 THEN
  435. Texts.Delete(Oberon.Log, Oberon.Log.len-1, Oberon.Log.len);
  436. DEC(i)
  437. END
  438. ELSE
  439. IF show THEN Texts.Write(w, ch) ELSE Texts.Write(w, "*") END;
  440. Texts.Append(Oberon.Log, w.buf);
  441. str[i] := ch; INC(i)
  442. END;
  443. Input.Read (ch)
  444. END;
  445. IF ch # 0DX THEN i := 0 END;
  446. str[i] := 0X;
  447. Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
  448. END InputStr;
  449. PROCEDURE Replace(p: Password);
  450. VAR q, prev: Password; msg: ARRAY 12 OF CHAR;
  451. BEGIN
  452. q := passwords; prev := NIL;
  453. WHILE (q # NIL) & ~((q.service = p.service) & (q.host = p.host) & (q.user = p.user)) DO
  454. prev := q; q := q.next
  455. END;
  456. IF q # NIL THEN (* password exists, delete old one first *)
  457. IF prev = NIL THEN passwords := passwords.next
  458. ELSE prev.next := q.next
  459. END;
  460. msg := "replaced"
  461. ELSE
  462. msg := "set"
  463. END;
  464. p.next := passwords; passwords := p;
  465. IF verbose THEN
  466. Texts.WriteString(w, p.service); Texts.Write(w, ":");
  467. Texts.WriteString(w, p.user); Texts.Write(w, "@"); Texts.WriteString(w, p.host);
  468. Texts.WriteString(w, " password "); Texts.WriteString(w, msg);
  469. Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
  470. END
  471. END Replace;
  472. BEGIN
  473. Texts.OpenReader(R, Oberon.Par.text, Oberon.Par.pos);
  474. ok := TRUE; entered[0] := 0X; verbose := FALSE;
  475. WHILE ~R.eot & ok DO
  476. ok := FALSE; Next(service);
  477. IF service = "\v" THEN verbose := TRUE; Next(service) END;
  478. Strings.Lower(service, service);
  479. IF ch = ":" THEN
  480. Next(usr);
  481. IF ch = ":" THEN (* password specified in-line *)
  482. Next(pwd);
  483. IF ch = "@" THEN Next(host) ELSE COPY(pwd, host); pwd[0] := 0X END
  484. ELSIF ch = "@" THEN (* no password specified in-line *)
  485. pwd[0] := 0X; Next(host)
  486. ELSE (* no user or password specified *)
  487. COPY(usr, host); usr[0] := 0X; pwd[0] := 0X
  488. END;
  489. Strings.Lower(host, host);
  490. IF host[0] # 0X THEN
  491. IF (usr[0] = 0X) OR ((pwd[0] = 0X) & (entered[0] = 0X)) THEN
  492. Texts.WriteString(w, service); Texts.WriteString(w, "://");
  493. IF usr[0] # 0X THEN Texts.WriteString(w, usr); Texts.Write(w, "@") END;
  494. Texts.WriteString(w, host); Texts.WriteLn(w)
  495. END;
  496. IF usr[0] = 0X THEN (* no user specified, prompt *)
  497. InputStr("Enter user name: ", TRUE, usr);
  498. IF usr[0] = 0X THEN RETURN END
  499. END;
  500. IF pwd[0] = 0X THEN (* no pwd specified *)
  501. IF entered[0] = 0X THEN (* prompt first time *)
  502. InputStr("Enter password: ", FALSE, entered);
  503. IF entered[0] = 0X THEN RETURN END (* esc *)
  504. END;
  505. pwd := entered
  506. END;
  507. NEW(pass); COPY(service, pass.service); COPY(host, pass.host);
  508. COPY(usr, pass.user); COPY(pwd, pass.passwd);
  509. Replace(pass); ok := TRUE
  510. END
  511. END
  512. END
  513. END SetUser;
  514. (** Command NetSystem.ClearUser ~ Clear all passwords from memory. *)
  515. PROCEDURE ClearUser*;
  516. BEGIN
  517. passwords := NIL
  518. END ClearUser;
  519. (** Command NetSystem.Start ~ Start up NetSystem. *)
  520. PROCEDURE Start*;
  521. BEGIN
  522. IF ~started THEN
  523. started := TRUE;
  524. IF IP.default # NIL THEN
  525. hostIP := IP.default.localAdr;
  526. END;
  527. END
  528. END Start;
  529. (** Command NetSystem.Stop ~ Shut down NetSystem. *)
  530. PROCEDURE Stop*;
  531. BEGIN
  532. IF started THEN
  533. hostName := ""; hostIP := IP.NilAdr;
  534. started := FALSE;
  535. Texts.WriteString(w, "NetSystem stopped");
  536. Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
  537. END
  538. END Stop;
  539. PROCEDURE WriteIPPortAdr(VAR w: Texts.Writer; adr: IP.Adr; port: LONGINT);
  540. VAR s: ARRAY 16 OF CHAR; i, j: LONGINT;
  541. BEGIN
  542. IP.AdrToStr(adr, s);
  543. i := 0; WHILE s[i] # 0X DO Texts.Write(w, s[i]); INC(i) END;
  544. IF port # -1 THEN
  545. Texts.Write(w, ":"); INC(i);
  546. Strings.IntToStr(port, s);
  547. j:= 0; WHILE s[j] # 0X DO Texts.Write(w, s[j]); INC(j); INC(i) END;
  548. j := 21
  549. ELSE
  550. j := 16
  551. END;
  552. WHILE i < j DO Texts.Write(w, " "); INC(i) END
  553. END WriteIPPortAdr;
  554. BEGIN
  555. state[TCP.Closed] := closed; state[TCP.Listen] := listening; state[TCP.SynSent] := waitCon;
  556. state[TCP.SynReceived] := inout; state[TCP.Established] := inout;
  557. state[TCP.CloseWait] := out; state[TCP.FinWait1] := inout; state[TCP.Closing] := closed;
  558. state[TCP.LastAck] := closed; state[TCP.FinWait2] := inout; state[TCP.TimeWait] := closed;
  559. anyIP := IP.NilAdr;
  560. allIP.usedProtocol := IP.IPv4;
  561. allIP.ipv4Adr := LONGINT(0FFFFFFFFH);
  562. crlf[0] := CR; crlf[1] := LF;
  563. started := FALSE; hostName := "";
  564. Texts.OpenWriter(w); passwords := NIL;
  565. Start;
  566. Modules.InstallTermHandler(Stop)
  567. END NetSystem.