SambaClient.Mod 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253
  1. MODULE SambaClient; (** AUTHOR "mancos"; PURPOSE "SMB Client"; *)
  2. IMPORT
  3. SYSTEM, Streams, KernelLog, Dates, Strings, Locks, Files, DNS, IP, TCP;
  4. CONST
  5. PID = 9876;
  6. NativeOS = "A2";
  7. NativeLanMan = "STECIFS";
  8. PrimaryDomain = "WORKGROUP";
  9. Trace = FALSE;
  10. SendBufferSize = 32000;
  11. RWLimit = 2048;
  12. SMBPort* = 445;
  13. TYPE
  14. Connection = POINTER TO RECORD
  15. out: Streams.Writer;
  16. in: Streams.Reader;
  17. tid, uid, sid: INTEGER;
  18. ipaddr: ARRAY 16 OF CHAR;
  19. user, pw: ARRAY 64 OF CHAR;
  20. path, mask, fnLast: ARRAY 256 OF CHAR;
  21. END;
  22. FileSystem* = OBJECT(Files.FileSystem) (** shareable *)
  23. VAR
  24. c: Connection;
  25. connection: TCP.Connection;
  26. lock: Locks.RecursiveLock;
  27. (** Create a new file with the specified name. End users use Files.New instead. *)
  28. PROCEDURE New0*(name: ARRAY OF CHAR): Files.File;
  29. VAR
  30. key: LONGINT;
  31. f: File;
  32. BEGIN
  33. IF Trace THEN KernelLog.String("FileSystem.New - Name: "); KernelLog.String(name); KernelLog.Ln(); END;
  34. lock.Acquire();
  35. key := OpenAndX(c, name, 41H, TRUE);
  36. lock.Release();
  37. IF key # 0 THEN
  38. NEW(f);
  39. f.fs := SELF;
  40. f.c := c;
  41. f.key := key;
  42. f.openRead := FALSE;
  43. COPY(name, f.filename);
  44. RETURN f;
  45. ELSE
  46. RETURN NIL;
  47. END;
  48. END New0;
  49. (** Open an existing file. The same file descriptor is returned if a file is opened multiple times. End users use Files.Old instead. *)
  50. PROCEDURE Old0*(name: ARRAY OF CHAR): Files.File;
  51. VAR
  52. f: File;
  53. key: LONGINT;
  54. BEGIN
  55. IF Trace THEN KernelLog.String("FileSystem.Old - Name: "); KernelLog.String(name); KernelLog.Ln(); END;
  56. key := FileKey(name);
  57. IF key # 0 THEN
  58. NEW(f);
  59. f.c := c;
  60. f.fs := SELF;
  61. f.key := key;
  62. f.openRead := TRUE;
  63. COPY(name, f.filename);
  64. RETURN f;
  65. ELSE
  66. RETURN NIL;
  67. END;
  68. END Old0;
  69. (** Delete a file. res = 0 indicates success. End users use Files.Delete instead. *)
  70. PROCEDURE Delete0*(name: ARRAY OF CHAR; VAR key: LONGINT; VAR res: WORD);
  71. VAR
  72. check : BOOLEAN;
  73. closekey : LONGINT;
  74. BEGIN
  75. IF Trace THEN
  76. KernelLog.String("FileSystem.Delete"); KernelLog.Ln();
  77. KernelLog.String(" -- Name: "); KernelLog.String(name); KernelLog.Ln();
  78. END;
  79. lock.Acquire();
  80. closekey := OpenAndX(c, name, 40H, FALSE);
  81. CloseFile(c, closekey);
  82. lock.Release();
  83. Strings.TrimLeft(name, CHR(2FH));
  84. lock.Acquire();
  85. (* SEND *)
  86. SendSMBHeader(SHORT(39 + Strings.Length(name)), 06X, c);
  87. c.out.Net8(1); (* Word count *)
  88. c.out.RawInt(22); (* search attributes *)
  89. c.out.RawInt(SHORT(2 + Strings.Length(name)));
  90. c.out.Net8(4); (* ascii *)
  91. c.out.RawString(name); (* name *)
  92. c.out.Update();
  93. (* RECEIVE *)
  94. check := RecieveResponse(06X, c);
  95. lock.Release();
  96. IF ~check THEN
  97. IF Trace THEN KernelLog.String(" -- ERROR on Delete"); KernelLog.Ln(); END;
  98. res := -1;
  99. ELSE
  100. res := 0;
  101. END;
  102. END Delete0;
  103. (** Rename a file. res = 0 indicates success. End users use Files.Rename instead. *)
  104. PROCEDURE Rename0*(old, new: ARRAY OF CHAR; f: Files.File; VAR res: WORD);
  105. VAR
  106. check : BOOLEAN;
  107. closekey : LONGINT;
  108. BEGIN
  109. IF Trace THEN
  110. KernelLog.String("FileSystem.Rename"); KernelLog.Ln();
  111. KernelLog.String(" -- Old: "); KernelLog.String(old); KernelLog.Ln();
  112. KernelLog.String(" -- New: "); KernelLog.String(new); KernelLog.Ln();
  113. END;
  114. lock.Acquire();
  115. closekey := OpenAndX(c, old, 40H, FALSE);
  116. CloseFile(c, closekey);
  117. lock.Release();
  118. ReplaceSlash(old);
  119. ReplaceSlash(new);
  120. lock.Acquire();
  121. (* SEND *)
  122. SendSMBHeader(SHORT(41 + Strings.Length(new) + Strings.Length(old)), 07X, c);
  123. c.out.Net8(1); (* Word count *)
  124. c.out.RawInt(22); (* search attributes *)
  125. c.out.RawInt(SHORT(4 + Strings.Length(new) + Strings.Length(old)));
  126. c.out.Net8(4); (* ascii *)
  127. c.out.RawString(old); (* old name *)
  128. c.out.Net8(4); (* ascii *)
  129. c.out.RawString(new); (* new name *)
  130. c.out.Update();
  131. (* RECEIVE *)
  132. check := RecieveResponse(07X, c);
  133. lock.Release();
  134. IF ~check THEN
  135. IF Trace THEN KernelLog.String(" -- ERROR on Rename"); KernelLog.Ln(); END;
  136. res := -1;
  137. ELSE
  138. res := 0;
  139. END;
  140. END Rename0;
  141. (** Enumerate canonical file names. mask may contain * wildcards. For internal use only. End users use Enumerator instead. *)
  142. PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator);
  143. VAR
  144. check, endOfSearch, findFirst: BOOLEAN;
  145. byteCount, dataOff, paraOff, eos: INTEGER;
  146. fileFlags : SET;
  147. attr, curPos, nextEntryOff, progress, maskLen: LONGINT;
  148. t: ARRAY 2 OF LONGINT;
  149. ch: ARRAY 2 OF CHAR;
  150. dt: Dates.DateTime;
  151. date, time, size: LONGINT;
  152. dirMask, filename: ARRAY 256 OF CHAR;
  153. BEGIN
  154. endOfSearch := FALSE;
  155. findFirst := TRUE;
  156. maskLen := Strings.Length(mask);
  157. COPY(mask, dirMask);
  158. ReplaceSlash(mask);
  159. IF Trace THEN
  160. KernelLog.String("FileSystem.Enumerate"); KernelLog.Ln();
  161. KernelLog.String(" -- Mask: "); KernelLog.String(mask); KernelLog.Ln();
  162. END;
  163. IF mask = "*" THEN
  164. ch[0] := CHR(92); ch[1] := 0X;
  165. Strings.Concat(ch, "*", c.mask);
  166. dirMask := "/";
  167. ELSIF Strings.EndsWith("\\*", mask) THEN
  168. Strings.Truncate(mask, maskLen - 2);
  169. Strings.Truncate(dirMask, maskLen - 2);
  170. Strings.Concat(mask, "*", c.mask);
  171. ELSIF Strings.EndsWith("\*", mask) THEN
  172. Strings.Truncate(mask, maskLen - 1);
  173. Strings.Truncate(dirMask, maskLen - 1);
  174. Strings.Concat(mask, "*", c.mask);
  175. ELSE
  176. RETURN;
  177. END;
  178. WHILE ~endOfSearch DO
  179. lock.Acquire();
  180. IF findFirst THEN
  181. check := Trans2Find(c, 1);
  182. ELSE
  183. check := Trans2Find(c, 2);
  184. END;
  185. IF ~check THEN
  186. IF Trace THEN KernelLog.String(" -- ERROR on Enumerate"); KernelLog.Ln(); END;
  187. lock.Release();
  188. RETURN;
  189. END;
  190. (* RECEIVE *)
  191. c.in.SkipBytes(1); (* wct *)
  192. c.in.SkipBytes(2); (* parameter count *)
  193. c.in.SkipBytes(2); (* data count *)
  194. c.in.SkipBytes(2); (* reserved *)
  195. c.in.SkipBytes(2); (* parameter count *)
  196. c.in.RawInt(paraOff); (* parameter offset *)
  197. c.in.SkipBytes(2); (* parameter displacement *)
  198. c.in.RawInt(byteCount); (* data count *)
  199. c.in.RawInt(dataOff); (* data offset *)
  200. c.in.SkipBytes(2); (* data displacement *)
  201. c.in.SkipBytes(1); (* setup count *)
  202. c.in.SkipBytes(1); (* reserved *)
  203. c.in.SkipBytes(2); (* byte count *)
  204. c.in.SkipBytes(paraOff - 55);
  205. IF findFirst THEN
  206. c.in.RawInt(c.sid); (* search id *)
  207. END;
  208. c.in.SkipBytes(2); (* search count *)
  209. c.in.RawInt(eos); (* end of search *)
  210. IF eos = 1 THEN
  211. endOfSearch := TRUE;
  212. END;
  213. c.in.SkipBytes(2); (* EA error *)
  214. c.in.SkipBytes(2); (* last name offset *)
  215. IF findFirst THEN
  216. c.in.SkipBytes(dataOff - paraOff - 10);
  217. ELSE
  218. c.in.SkipBytes(dataOff - paraOff - 8);
  219. END;
  220. progress := 0;
  221. check := TRUE;
  222. WHILE progress < byteCount DO
  223. curPos := c.in.Pos();
  224. c.in.RawLInt(nextEntryOff);
  225. IF nextEntryOff = 0 THEN
  226. check := FALSE;
  227. progress := byteCount;
  228. ELSE
  229. progress := progress + nextEntryOff; (* next entry offset *)
  230. curPos := curPos + nextEntryOff;
  231. END;
  232. c.in.SkipBytes(4); (* file index *)
  233. c.in.SkipBytes(8); (* creation time *)
  234. c.in.SkipBytes(8); (* last access *)
  235. c.in.RawLInt(t[0]);
  236. c.in.RawLInt(t[1]); (* last write *)
  237. GetDateTime(t, dt);
  238. IF ~Dates.ValidDateTime(dt) THEN
  239. dt := Dates.Now();
  240. KernelLog.String("SambaClient: Replaced invalid date & time by current time"); KernelLog.Ln;
  241. END;
  242. Dates.DateTimeToOberon(dt, date, time);
  243. c.in.SkipBytes(8); (* change *)
  244. c.in.RawLInt(size);
  245. c.in.SkipBytes(4); (* end of file *)
  246. c.in.SkipBytes(8); (* allocation *)
  247. c.in.RawLInt(attr); (* attributes *)
  248. fileFlags := {};
  249. IF (4 IN SYSTEM.VAL(SET, attr)) THEN INCL(fileFlags, Files.Directory); END;
  250. IF (0 IN SYSTEM.VAL(SET, attr)) THEN INCL(fileFlags, Files.ReadOnly); END;
  251. c.in.SkipBytes(4); (* name len *)
  252. c.in.SkipBytes(4); (* ea len *)
  253. c.in.SkipBytes(1); (* short file len *)
  254. c.in.SkipBytes(1); (* reserved *)
  255. c.in.SkipBytes(24); (* short file name *)
  256. c.in.RawString(filename);
  257. COPY(filename, c.fnLast);
  258. Strings.Concat(dirMask, filename, filename);
  259. Files.JoinName(prefix, filename, filename);
  260. enum.PutEntry(filename, fileFlags, time, date, size);
  261. WHILE (c.in.Pos() # curPos) & check DO
  262. c.in.SkipBytes(1);
  263. END;
  264. END;
  265. findFirst := FALSE;
  266. lock.Release();
  267. END;
  268. END Enumerate0;
  269. (** Return the unique non-zero key of the named file, if it exists. *)
  270. PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
  271. VAR key: LONGINT;
  272. BEGIN
  273. lock.Acquire();
  274. key := OpenAndX(c, name, 40H, FALSE);
  275. lock.Release();
  276. RETURN key;
  277. END FileKey;
  278. (** Create a new directory structure. May not be supported by the actual implementation.
  279. End users use Files.CreateDirectory instead.*)
  280. PROCEDURE CreateDirectory0*(name: ARRAY OF CHAR; VAR res: WORD);
  281. VAR check: BOOLEAN;
  282. BEGIN
  283. IF Trace THEN
  284. KernelLog.String("FileSystem.CreateDirectory"); KernelLog.Ln();
  285. KernelLog.String(" -- Name: "); KernelLog.String(name); KernelLog.Ln();
  286. END;
  287. lock.Acquire();
  288. (* SEND *)
  289. SendSMBHeader(SHORT(37+ Strings.Length(name)), 00X, c);
  290. c.out.Net8(0); (* Word count *)
  291. c.out.RawInt(SHORT(2 + Strings.Length(name))); (* byte count *)
  292. c.out.Net8(4); (* buffer format *)
  293. c.out.RawString(name);
  294. c.out.Update();
  295. (* RECEIVE *)
  296. check := RecieveResponse(00X, c);
  297. lock.Release();
  298. IF ~check THEN
  299. IF Trace THEN KernelLog.String(" -- ERROR on CreateDirectory"); KernelLog.Ln(); END;
  300. res := -1;
  301. ELSE
  302. res := 0;
  303. END;
  304. END CreateDirectory0;
  305. (** Remove a directory. If force=TRUE, any subdirectories and files should be automatically deleted.
  306. End users use Files.RemoveDirectory instead. *)
  307. PROCEDURE RemoveDirectory0*(name: ARRAY OF CHAR; force: BOOLEAN; VAR key: LONGINT; VAR res: WORD);
  308. VAR check: BOOLEAN;
  309. BEGIN
  310. IF Trace THEN
  311. KernelLog.String("FileSystem.DeleteDirectory"); KernelLog.Ln();
  312. KernelLog.String(" -- Name: "); KernelLog.String(name); KernelLog.Ln();
  313. END;
  314. lock.Acquire();
  315. (* SEND *)
  316. SendSMBHeader(SHORT(37+ Strings.Length(name)), 01X, c);
  317. c.out.Net8(0); (* Word count *)
  318. c.out.RawInt(SHORT(2 + Strings.Length(name))); (* byte count *)
  319. c.out.Net8(4); (* buffer format *)
  320. c.out.RawString(name);
  321. c.out.Update();
  322. (* RECEIVE *)
  323. check := RecieveResponse(01X, c);
  324. lock.Release();
  325. IF ~check THEN
  326. IF Trace THEN KernelLog.String(" -- ERROR on DeleteDirectory"); KernelLog.Ln(); END;
  327. res := -1;
  328. ELSE
  329. res := 0;
  330. END;
  331. END RemoveDirectory0;
  332. (** Finalize the file system. *)
  333. PROCEDURE Finalize*;
  334. BEGIN
  335. Finalize^;
  336. connection.Close();
  337. END Finalize;
  338. END FileSystem;
  339. TYPE
  340. File* = OBJECT(Files.File) (** sharable *)
  341. VAR
  342. c: Connection;
  343. filename: ARRAY 256 OF CHAR;
  344. openRead: BOOLEAN;
  345. (** Position a Rider at a certain position in a file. Multiple Riders can be positioned at different locations in a file. A Rider cannot be positioned beyond the end of a file. *)
  346. PROCEDURE Set*(VAR r: Files.Rider; pos: LONGINT);
  347. BEGIN
  348. r.apos := pos;
  349. r.file := SELF;
  350. END Set;
  351. (** Return the offset of a Rider positioned on a file. *)
  352. PROCEDURE Pos*(VAR r: Files.Rider): LONGINT;
  353. BEGIN
  354. RETURN r.apos;
  355. END Pos;
  356. (** Read a byte from a file, advancing the Rider one byte further. R.eof indicates if the end of the file has been passed. *)
  357. PROCEDURE Read*(VAR r: Files.Rider; VAR x: CHAR);
  358. VAR a : ARRAY 1 OF CHAR;
  359. BEGIN
  360. a[0] := x;
  361. ReadBytes(r,a,0,1);
  362. END Read;
  363. (** Read a sequence of len bytes into the buffer x at offset ofs, advancing the Rider. Less bytes will be read when reading over the end of the file. r.res indicates the number of unread bytes. x must be big enough to hold all the bytes. *)
  364. PROCEDURE ReadBytes*(VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT);
  365. VAR
  366. check: BOOLEAN;
  367. dataOff, byteCount, padding: INTEGER;
  368. i : LONGINT;
  369. adjLen, adjOff: INTEGER;
  370. localKey: INTEGER;
  371. BEGIN
  372. IF Trace THEN KernelLog.String("File.ReadBytes - Read AndX"); KernelLog.Ln(); END;
  373. fs(FileSystem).lock.Acquire();
  374. IF ~(openRead & (key # 0)) THEN
  375. localKey := OpenAndX(c, filename, 40H, FALSE);
  376. IF localKey # 0 THEN
  377. key := localKey;
  378. openRead := TRUE;
  379. ELSE
  380. r.res := len;
  381. fs(FileSystem).lock.Release();
  382. RETURN;
  383. END;
  384. END;
  385. adjOff := 0;
  386. r.res := 0;
  387. WHILE len > 0 DO
  388. IF len > RWLimit THEN
  389. adjLen := RWLimit;
  390. ELSE
  391. adjLen := SHORT(len);
  392. END;
  393. (* SEND *)
  394. SendSMBHeader(55, 2EX, c);
  395. c.out.Net8(10); (* word count *)
  396. c.out.Net8(255); (* andx*)
  397. c.out.Net8(0); (* reserved *)
  398. c.out.RawInt(0); (* andx offset *)
  399. c.out.RawInt(SHORT(key)); (* fid *)
  400. c.out.RawLInt(r.apos); (* offset *)
  401. c.out.RawInt(adjLen); (* max count low *)
  402. c.out.RawInt(adjLen); (* min count *)
  403. c.out.Net32(0); (* max count 64k *)
  404. c.out.RawInt(0); (* remaining *)
  405. c.out.RawInt(0); (* byte count *)
  406. c.out.Update();
  407. (* RECEIVE *)
  408. check := RecieveResponse(2EX, c);
  409. IF ~check THEN
  410. IF Trace THEN KernelLog.String(" -- ERROR on Read AndX"); KernelLog.Ln(); END;
  411. r.res := r.res + len;
  412. fs(FileSystem).lock.Release();
  413. RETURN;
  414. END;
  415. c.in.SkipBytes(13);
  416. c.in.RawInt(dataOff);
  417. c.in.SkipBytes(10);
  418. c.in.RawInt(byteCount);
  419. IF (dataOff = 0) THEN
  420. r.res := r.res + len;
  421. fs(FileSystem).lock.Release();
  422. RETURN;
  423. END;
  424. padding := dataOff - 59;
  425. c.in.SkipBytes(padding);
  426. byteCount := byteCount - padding;
  427. IF Trace THEN KernelLog.String(" -- ByteCount: "); KernelLog.Int(byteCount, 0); KernelLog.Ln(); END;
  428. i := adjOff;
  429. c.in.Bytes(x, ofs, byteCount, i);
  430. ofs := ofs + i;
  431. r.apos := r.apos + i;
  432. r.res := r.res + adjLen - i;
  433. len := len - adjLen;
  434. adjOff := adjOff + adjLen;
  435. END;
  436. fs(FileSystem).lock.Release();
  437. END ReadBytes;
  438. (** Write a byte into the file at the Rider position, advancing the Rider by one. *)
  439. PROCEDURE Write*(VAR r: Files.Rider; x: CHAR);
  440. VAR
  441. a: ARRAY 1 OF CHAR;
  442. BEGIN
  443. a[0] := x;
  444. WriteBytes(r,a,0,1);
  445. END Write;
  446. (** Write the buffer x containing len bytes (starting at offset ofs) into a file at the Rider position. *)
  447. PROCEDURE WriteBytes*(VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT);
  448. VAR
  449. check: BOOLEAN;
  450. bytesWritten: INTEGER;
  451. adjLen, adjOff: INTEGER;
  452. localKey: INTEGER;
  453. BEGIN
  454. fs(FileSystem).lock.Acquire();
  455. IF ~(~openRead & (key # 0)) THEN
  456. localKey := OpenAndX(c, filename, 41H, FALSE);
  457. IF localKey # 0 THEN
  458. key := localKey;
  459. openRead := FALSE;
  460. ELSE
  461. r.res := len;
  462. fs(FileSystem).lock.Release();
  463. RETURN;
  464. END;
  465. END;
  466. IF Trace THEN
  467. KernelLog.String("FileSystem.WriteBytes - Write AndX");
  468. KernelLog.String(" ("); KernelLog.Int(len, 0); KernelLog.String(" bytes from offset ");
  469. KernelLog.Int(ofs, 0); KernelLog.String(")");
  470. KernelLog.Ln();
  471. END;
  472. fs(FileSystem).lock.Acquire();
  473. adjOff := 0;
  474. WHILE len > 0 DO
  475. IF len > RWLimit THEN
  476. adjLen := RWLimit;
  477. ELSE
  478. adjLen := SHORT(len);
  479. END;
  480. (* Send *)
  481. SendSMBHeader(59 + adjLen, 2FX, c);
  482. c.out.Net8(12); (* word count *)
  483. c.out.Net8(255); (* andx*)
  484. c.out.Net8(0); (* reserved *)
  485. c.out.RawInt(0); (* andx offset *)
  486. c.out.RawInt(SHORT(key)); (* fid *)
  487. c.out.RawLInt(r.apos); (* offset *)
  488. c.out.RawLInt(0); (* reserved *)
  489. c.out.RawInt(0); (* write mode *)
  490. c.out.RawInt(0); (* remaining *)
  491. c.out.RawInt(0); (* max count 64k *)
  492. c.out.RawInt(adjLen); (* max count low *)
  493. c.out.RawInt(59); (* data offset*)
  494. c.out.RawInt(adjLen); (* byte count *)
  495. IF adjLen # 0 THEN
  496. IF Trace THEN KernelLog.String(" -- Write bytes: "); KernelLog.Int(adjLen, 0); KernelLog.Ln(); END;
  497. c.out.Bytes(x, adjOff, adjLen);
  498. ELSE
  499. IF Trace THEN KernelLog.String(" -- No bytes written!"); KernelLog.Ln(); END;
  500. END;
  501. c.out.Update();
  502. (* RECEIVE *)
  503. check := RecieveResponse(2FX, c);
  504. IF ~check THEN
  505. IF Trace THEN KernelLog.String(" -- ERROR on Write AndX"); KernelLog.Ln(); END;
  506. fs(FileSystem).lock.Release();
  507. RETURN;
  508. END;
  509. c.in.SkipBytes(5);
  510. c.in.RawInt(bytesWritten);
  511. IF Trace THEN
  512. KernelLog.String(" -- Bytes written: "); KernelLog.Int(bytesWritten, 0); KernelLog.Ln();
  513. END;
  514. r.apos := r.apos + bytesWritten;
  515. len := len - adjLen;
  516. adjOff := adjOff + adjLen;
  517. END;
  518. fs(FileSystem).lock.Release();
  519. END WriteBytes;
  520. (** Return the current length of a file. *)
  521. PROCEDURE Length*(): LONGINT;
  522. VAR
  523. filesize: LONGINT;
  524. check: BOOLEAN;
  525. BEGIN
  526. fs(FileSystem).lock.Acquire();
  527. IF Trace THEN
  528. KernelLog.String("File.Length"); KernelLog.Ln();
  529. KernelLog.String(" -- Name: "); KernelLog.String(filename); KernelLog.Ln();
  530. END;
  531. (* SEND *)
  532. SendSMBHeader(37 + SHORT(Strings.Length(filename)), 08X, c);
  533. c.out.Net8(0); (* word count *)
  534. c.out.RawInt(SHORT(Strings.Length(filename)) + 1); (* byte count *)
  535. c.out.Net8(4); (* buffer format ascii *)
  536. c.out.RawString(filename); (* filename *)
  537. c.out.Update();
  538. IF Trace THEN KernelLog.String(" -- Query Information Request sent"); KernelLog.Ln(); END;
  539. (* RECEIVE *)
  540. check := RecieveResponse(08X, c);
  541. IF ~check THEN
  542. IF Trace THEN
  543. KernelLog.String(" -- ERROR on Query Information Request"); KernelLog.Ln();
  544. END;
  545. fs(FileSystem).lock.Release();
  546. RETURN 0;
  547. END;
  548. c.in.SkipBytes(7);
  549. c.in.RawLInt(filesize);
  550. fs(FileSystem).lock.Release();
  551. IF Trace THEN KernelLog.String(" -- File size: "); KernelLog.Int(filesize, 0); KernelLog.Ln(); END;
  552. RETURN filesize;
  553. END Length;
  554. (** Return the time (t) and date (d) when a file was last modified. *)
  555. PROCEDURE GetDate*(VAR t, d: LONGINT);
  556. BEGIN HALT(301) END GetDate; (* abstract *)
  557. (** Set the modification time (t) and date (d) of a file. *)
  558. PROCEDURE SetDate*(t, d: LONGINT);
  559. BEGIN HALT(301) END SetDate; (* abstract *)
  560. (** Return the canonical name of a file. *)
  561. PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
  562. BEGIN
  563. Files.JoinName(fs.prefix, filename, name);
  564. IF Trace THEN
  565. KernelLog.String("File.GetName"); KernelLog.Ln();
  566. KernelLog.String(" -- Name: "); KernelLog.String(name); KernelLog.Ln();
  567. END;
  568. END GetName;
  569. (** Register a file created with New in the directory, replacing the previous file in the directory with the same name. The file is automatically updated. End users use Files.Register instead. *)
  570. PROCEDURE Register0*(VAR res: WORD);
  571. BEGIN END Register0;
  572. (** Flush the changes made to a file from its buffers. Register0 will automatically update a file. *)
  573. PROCEDURE Update*;
  574. BEGIN END Update;
  575. END File;
  576. TYPE
  577. TCPSender = OBJECT
  578. VAR
  579. connection: TCP.Connection;
  580. PROCEDURE Connect(CONST host: ARRAY OF CHAR; port: LONGINT; VAR c: Connection);
  581. VAR
  582. fadr: IP.Adr;
  583. res: WORD;
  584. BEGIN {EXCLUSIVE}
  585. res := 0;
  586. DNS.HostByName(host, fadr, res);
  587. IF res = DNS.Ok THEN
  588. NEW(connection);
  589. connection.Open(TCP.NilPort, fadr, port, res);
  590. IF res = TCP.Ok THEN
  591. IF Trace THEN KernelLog.String("Connection open!"); KernelLog.Ln(); END;
  592. NEW(c.out, connection.Send, SendBufferSize);
  593. NEW(c.in, connection.Receive, SendBufferSize);
  594. END;
  595. END;
  596. END Connect;
  597. END TCPSender;
  598. PROCEDURE SendSMBHeader(ntb: INTEGER; cmd: CHAR; c: Connection);
  599. BEGIN
  600. (* NETBIOS *)
  601. c.out.Net16(0);
  602. c.out.Net16(ntb);
  603. (* SMB *)
  604. c.out.Char(CHR(255));
  605. c.out.String("SMB");
  606. c.out.Char(cmd); (* Command *)
  607. c.out.Net32(0); (* status code *)
  608. c.out.Net8(24); (* FLAGS *)
  609. c.out.RawInt(1); (* FLAGS 2 *)
  610. c.out.Net32(0);
  611. c.out.Net32(0); (* EXTRA *)
  612. c.out.Net32(0);
  613. c.out.RawInt(c.tid); (* TID *)
  614. c.out.RawInt(PID); (* PID *)
  615. c.out.RawInt(c.uid); (* UID *)
  616. c.out.RawInt(0); (* MID *)
  617. END SendSMBHeader;
  618. PROCEDURE RecieveResponse(cmd: CHAR; c: Connection): BOOLEAN;
  619. VAR
  620. check: BOOLEAN;
  621. variable: INTEGER;
  622. BEGIN
  623. check := FALSE;
  624. c.in.Reset();
  625. (* NETBIOS *)
  626. c.in.SkipBytes(4);
  627. (* SMB *)
  628. check := CheckFFSMB(c);
  629. IF ~check THEN
  630. IF Trace THEN KernelLog.String("SMB Header does not start with 0xFF SMB"); KernelLog.Ln(); END;
  631. c.in.Reset();
  632. RETURN FALSE;
  633. END;
  634. variable := SHORT(c.in.Net8());
  635. IF CHR(variable) # cmd THEN
  636. IF Trace THEN KernelLog.String("SMB Command is NOT "); KernelLog.Char(cmd); KernelLog.Ln(); END;
  637. c.in.Reset();
  638. RETURN FALSE;
  639. END;
  640. variable := SHORT(c.in.Net32());
  641. IF variable # 0 THEN
  642. IF Trace THEN KernelLog.String("There has been a DOS error"); KernelLog.Ln(); END;
  643. c.in.Reset();
  644. RETURN FALSE;
  645. END;
  646. c.in.SkipBytes(15);
  647. c.in.RawInt(variable);
  648. IF (c.tid = 0) & (variable > 0) THEN
  649. c.tid := variable;
  650. ELSIF (c.tid = variable) THEN
  651. (* OK *)
  652. ELSIF (c.tid # variable) THEN
  653. IF Trace THEN KernelLog.String(" -- TID does not match"); KernelLog.Ln(); END;
  654. RETURN FALSE;
  655. ELSE
  656. IF Trace THEN KernelLog.String(" -- TID Error "); KernelLog.Int(variable, 0); KernelLog.Ln(); END;
  657. RETURN FALSE;
  658. END;
  659. c.in.RawInt(variable);
  660. IF variable # PID THEN
  661. IF Trace THEN KernelLog.String(" -- PID does not match"); KernelLog.Ln(); END;
  662. RETURN FALSE;
  663. END;
  664. c.in.RawInt(variable);
  665. IF (c.uid = 0) & (variable > 0) THEN
  666. c.uid := variable;
  667. ELSIF (c.uid = variable) THEN
  668. (* OK *)
  669. ELSIF (c.uid # variable) THEN
  670. IF Trace THEN KernelLog.String(" -- UID does not match"); KernelLog.Ln(); END;
  671. RETURN FALSE;
  672. ELSE
  673. IF Trace THEN KernelLog.String(" -- UID Error "); KernelLog.Int(variable, 0); KernelLog.Ln(); END;
  674. RETURN FALSE;
  675. END;
  676. c.in.SkipBytes(2);
  677. RETURN TRUE;
  678. END RecieveResponse;
  679. PROCEDURE CheckFFSMB(c: Connection): BOOLEAN;
  680. VAR
  681. variable: SHORTINT;
  682. BEGIN
  683. c.in.RawSInt(variable);
  684. IF variable = -1 THEN
  685. c.in.RawSInt(variable);
  686. IF variable = 83 THEN
  687. c.in.RawSInt(variable);
  688. IF variable = 77 THEN
  689. c.in.RawSInt(variable);
  690. IF variable = 66 THEN
  691. RETURN TRUE;
  692. END;
  693. END;
  694. END;
  695. END;
  696. RETURN FALSE;
  697. END CheckFFSMB;
  698. PROCEDURE ReplaceSlash(VAR name: ARRAY OF CHAR);
  699. VAR
  700. i: LONGINT;
  701. BEGIN
  702. i := 0;
  703. WHILE (i < Strings.Length(name)) DO
  704. IF name[i] = CHR(2FH) THEN
  705. name[i] := CHR(5CH);
  706. END;
  707. INC(i)
  708. END;
  709. END ReplaceSlash;
  710. PROCEDURE NegotiateProtocol(c: Connection): BOOLEAN;
  711. VAR
  712. check : BOOLEAN;
  713. variable: LONGINT;
  714. BEGIN
  715. IF Trace THEN KernelLog.String("Negotiate Protocol"); KernelLog.Ln(); END;
  716. (* SEND *)
  717. SendSMBHeader(47, 72X, c);
  718. c.out.Net8(0); (* Word count *)
  719. c.out.Net8(12); (* Byte count *)
  720. c.out.Net8(0);
  721. c.out.Char(2X);
  722. c.out.RawString("NT LM 0.12");
  723. c.out.Update();
  724. (* RECEIVE *)
  725. check := RecieveResponse(72X, c);
  726. IF ~check THEN
  727. RETURN FALSE;
  728. END;
  729. variable := c.in.Net8();
  730. IF variable # 17 THEN
  731. IF Trace THEN KernelLog.String(" -- Message Size is not 17: "); KernelLog.Int(variable, 2); KernelLog.Ln(); END;
  732. RETURN FALSE;
  733. ELSE
  734. IF Trace THEN KernelLog.String(" -- Message Size is 17: NT LM 0.12"); KernelLog.Ln(); END;
  735. RETURN TRUE;
  736. END;
  737. END NegotiateProtocol;
  738. PROCEDURE SessionSetup(c: Connection): BOOLEAN;
  739. VAR
  740. byteCount: INTEGER;
  741. check : BOOLEAN;
  742. BEGIN
  743. IF Trace THEN KernelLog.String("Session Setup"); KernelLog.Ln(); END;
  744. (* SEND *)
  745. byteCount := SHORT(
  746. Strings.Length(c.user)
  747. + Strings.Length(c.pw)
  748. + Strings.Length(PrimaryDomain)
  749. + Strings.Length(NativeOS)
  750. + Strings.Length(NativeLanMan));
  751. SendSMBHeader(66 + byteCount, 73X, c);
  752. c.out.Net8(13); (* Word count *)
  753. c.out.Net8(255); (* no andx *)
  754. c.out.Net8(0); (* reserved *)
  755. c.out.RawInt(0); (* andx offset *)
  756. c.out.RawInt(32767); (* buffersize *)
  757. c.out.RawInt(2); (* mpx *)
  758. c.out.RawInt(0); (* Vc *)
  759. c.out.Net32(0); (* session key *)
  760. c.out.RawInt(SHORT(Strings.Length(c.pw)+1)); (* ANSI len *)
  761. c.out.RawInt(0); (* UNICODE len *)
  762. c.out.Net32(0); (* reserved *)
  763. c.out.Net32(268435456); (* capabilities *)
  764. c.out.Net8(byteCount + 5);
  765. c.out.Net8(0);
  766. c.out.RawString(c.pw);
  767. c.out.RawString(c.user);
  768. c.out.RawString(PrimaryDomain);
  769. c.out.RawString(NativeOS);
  770. c.out.RawString(NativeLanMan);
  771. c.out.Update();
  772. (* RECEIVE *)
  773. check := RecieveResponse(73X, c);
  774. IF ~check THEN
  775. RETURN FALSE;
  776. END;
  777. IF Trace THEN KernelLog.String(" -- UID: "); KernelLog.Int(c.uid, 0); KernelLog.Ln(); END;
  778. RETURN TRUE;
  779. END SessionSetup;
  780. PROCEDURE TreeConnect(c: Connection): BOOLEAN;
  781. VAR
  782. check : BOOLEAN;
  783. BEGIN
  784. IF Trace THEN KernelLog.String("Tree Connect"); KernelLog.Ln(); END;
  785. (* SEND *)
  786. SendSMBHeader(54 + SHORT(Strings.Length(c.ipaddr) + Strings.Length(c.path) + Strings.Length(c.pw)), 75X, c);
  787. c.out.Net8(4); (* Word count *)
  788. c.out.Net8(255); (* no andx *)
  789. c.out.Net8(0); (* reserved *)
  790. c.out.RawInt(0); (* andx offset *)
  791. c.out.RawInt(0); (* disconnected tid *)
  792. c.out.RawInt(SHORT(Strings.Length(c.pw)+1)); (* pw length *)
  793. c.out.RawInt(SHORT(11 + Strings.Length(c.ipaddr) + Strings.Length(c.path) + Strings.Length(c.pw))); (* bcc *)
  794. c.out.RawString(c.pw);
  795. c.out.String("\\");
  796. c.out.String(c.ipaddr);
  797. c.out.String("\");
  798. c.out.RawString(c.path);
  799. c.out.RawString("?????");
  800. c.out.Update();
  801. (* RECEIVE *)
  802. check := RecieveResponse(75X, c);
  803. IF ~check THEN
  804. RETURN FALSE;
  805. END;
  806. IF Trace THEN KernelLog.String(" -- TID : "); KernelLog.Int(c.tid, 0); KernelLog.Ln(); END;
  807. RETURN TRUE;
  808. END TreeConnect;
  809. PROCEDURE Trans2Find(c: Connection; cmd: INTEGER): BOOLEAN;
  810. VAR
  811. check : BOOLEAN;
  812. len: INTEGER;
  813. BEGIN
  814. IF Trace THEN KernelLog.String("TRANS 2 - "); END;
  815. IF cmd = 1 THEN (* FIND FIRST *)
  816. len := SHORT(Strings.Length(c.mask));
  817. IF Trace THEN KernelLog.String("FIND FIRST"); KernelLog.Ln; END;
  818. ELSIF cmd = 2 THEN (* FIND NEXT *)
  819. len := SHORT(Strings.Length(c.fnLast));
  820. IF Trace THEN KernelLog.String("FIND NEXT"); KernelLog.Ln; END;
  821. ELSE
  822. RETURN FALSE;
  823. END;
  824. (* SEND *)
  825. SendSMBHeader(81 + len, 32X, c);
  826. c.out.Net8(15); (* Word count *)
  827. c.out.RawInt(13+len); (* parameter count 18 *)
  828. c.out.RawInt(0); (* data count *)
  829. c.out.RawInt(10); (* max par count *)
  830. c.out.RawInt(-1); (* max data count *)
  831. c.out.Net8(0); (* max setup *)
  832. c.out.Net8(0); (* reserved *)
  833. c.out.RawInt(0); (* flags *)
  834. c.out.Net32(0); (* timeout *)
  835. c.out.RawInt(0); (* reserved *)
  836. c.out.RawInt(13+len); (* parameter count 18 *)
  837. c.out.RawInt(68); (* par offset *)
  838. c.out.RawInt(0); (* data count *)
  839. c.out.RawInt(0); (* data offset 86 *)
  840. c.out.Net8(1); (* setup count *)
  841. c.out.Net8(0); (* reserved *)
  842. c.out.RawInt(cmd); (* subcommand *)
  843. c.out.RawInt(16+len); (* byte cnt 21 *)
  844. c.out.Net8(0); (* padding *)
  845. c.out.Net8(0); (* padding *)
  846. c.out.Net8(0); (* padding *)
  847. IF cmd = 1 THEN
  848. c.out.RawInt(22); (* search attri *)
  849. c.out.RawInt(25); (* search count 10 *)
  850. c.out.RawInt(6); (* flags *)
  851. c.out.RawInt(260); (* loi *)
  852. c.out.Net32(0); (* storage *)
  853. c.out.RawString(c.mask);
  854. ELSIF cmd = 2 THEN
  855. c.out.RawInt(c.sid); (* sid *)
  856. c.out.RawInt(25); (* search count 10 *)
  857. c.out.RawInt(260); (* loi *)
  858. c.out.Net32(0); (* resume *)
  859. c.out.RawInt(6); (* flags *)
  860. c.out.RawString(c.fnLast);
  861. ELSE
  862. RETURN FALSE;
  863. END;
  864. c.out.Update();
  865. (* RECEIVE *)
  866. check := RecieveResponse(32X, c);
  867. IF ~check THEN
  868. RETURN FALSE;
  869. END;
  870. RETURN TRUE;
  871. END Trans2Find;
  872. PROCEDURE OpenAndX(c: Connection; name: ARRAY OF CHAR; access: INTEGER; create: BOOLEAN): INTEGER;
  873. VAR
  874. check: BOOLEAN;
  875. fid: INTEGER;
  876. BEGIN
  877. IF Trace THEN
  878. KernelLog.String("Open AndX"); KernelLog.Ln();
  879. KernelLog.String(" -- Name: "); KernelLog.String(name); KernelLog.Ln();
  880. KernelLog.String(" -- Access: "); KernelLog.Int(access, 0); KernelLog.Ln();
  881. KernelLog.String(" -- Create: "); KernelLog.Boolean(create); KernelLog.Ln();
  882. END;
  883. ReplaceSlash(name);
  884. (* SEND *)
  885. SendSMBHeader(66 + SHORT(Strings.Length(name)), 2DX, c);
  886. c.out.Net8(15); (* word count *)
  887. c.out.Net8(255); (* andx*)
  888. c.out.Net8(0); (* reserved *)
  889. c.out.RawInt(0); (* andx offset *)
  890. c.out.RawInt(0); (* flags *)
  891. c.out.RawInt(access); (* desired access *)
  892. c.out.RawInt(6); (* search attributes *)
  893. c.out.RawInt(0); (* file attributes *)
  894. c.out.Net32(0); (* create.time *)
  895. IF create THEN
  896. c.out.RawInt(17);
  897. ELSE
  898. c.out.RawInt(1); (* open function *)
  899. END;
  900. c.out.Net32(0); (* allocation size *)
  901. c.out.Net32(0); (* timeout *)
  902. c.out.Net32(0); (* reserved *)
  903. c.out.RawInt(1 + SHORT(Strings.Length(name))); (* byte count *)
  904. c.out.RawString(name);
  905. c.out.Update();
  906. (* RECEIVE *)
  907. check := RecieveResponse(2DX, c);
  908. IF ~check THEN
  909. IF Trace THEN KernelLog.String(" -- ERROR on Open AndX - FID: 0"); KernelLog.Ln(); END;
  910. RETURN 0;
  911. END;
  912. c.in.SkipBytes(5);
  913. c.in.RawInt(fid);
  914. IF Trace THEN KernelLog.String(" -- FID: "); KernelLog.Int(fid, 0); KernelLog.Ln(); END;
  915. RETURN fid;
  916. END OpenAndX;
  917. PROCEDURE CloseFile(c: Connection; key: LONGINT);
  918. VAR
  919. check: BOOLEAN;
  920. BEGIN
  921. IF Trace THEN KernelLog.String("Close File"); KernelLog.Ln(); END;
  922. (* SEND *)
  923. SendSMBHeader(41, 04X, c);
  924. c.out.Net8(3); (* Word count *)
  925. c.out.RawInt(SHORT(key)); (* fid *)
  926. c.out.Net32(0); (* last write *)
  927. c.out.RawInt(0); (* byte count *)
  928. c.out.Update();
  929. (* RECEIVE *)
  930. check := RecieveResponse(04X, c);
  931. END CloseFile;
  932. PROCEDURE NewFS*(context: Files.Parameters);
  933. VAR
  934. fs: FileSystem;
  935. connection: TCP.Connection;
  936. c: Connection;
  937. check: BOOLEAN;
  938. BEGIN
  939. IF Files.This(context.prefix) = NIL THEN
  940. NEW(c);
  941. context.arg.SkipWhitespace; context.arg.String(c.ipaddr);
  942. context.arg.SkipWhitespace; context.arg.String(c.path);
  943. context.arg.SkipWhitespace; context.arg.String(c.user);
  944. context.arg.SkipWhitespace; context.arg.String(c.pw);
  945. IF Trace THEN
  946. KernelLog.String("Connecting to "); KernelLog.String(c.ipaddr); KernelLog.Ln();
  947. KernelLog.String("Path: "); KernelLog.String(c.path); KernelLog.Ln();
  948. END;
  949. check := StartClient(c, connection);
  950. IF (~check) OR (connection = NIL) THEN
  951. context.error.String("CONNECTION ERROR!"); context.error.Ln;
  952. RETURN;
  953. END;
  954. NEW(fs);
  955. fs.desc := "SmbFS";
  956. fs.c := c;
  957. NEW(fs.lock);
  958. fs.connection := connection;
  959. Files.Add(fs, context.prefix);
  960. ELSE
  961. context.error.String("DiskFS: "); context.error.String(context.prefix); context.error.String(" already in use");
  962. context.error.Ln;
  963. END;
  964. END NewFS;
  965. PROCEDURE StartClient(VAR c: Connection; VAR connection: TCP.Connection): BOOLEAN;
  966. VAR
  967. tcpsender: TCPSender;
  968. check: BOOLEAN;
  969. BEGIN
  970. c.tid := 0;
  971. c.uid := 0;
  972. NEW(tcpsender);
  973. tcpsender.Connect(c.ipaddr, SMBPort, c);
  974. IF (c.in # NIL) & (c.out # NIL) THEN
  975. connection := tcpsender.connection;
  976. check := NegotiateProtocol(c);
  977. check := check & SessionSetup(c);
  978. check := check & TreeConnect(c);
  979. RETURN check;
  980. ELSE
  981. RETURN FALSE;
  982. END;
  983. END StartClient;
  984. PROCEDURE GetDateTime(t: ARRAY OF LONGINT; VAR datetime: Dates.DateTime);
  985. VAR
  986. second, minute, hour, day, month, year, totalDays, NofDaysMnth: LONGINT;
  987. tsh: HUGEINT;
  988. ts: LONGINT;
  989. continue: BOOLEAN;
  990. BEGIN
  991. tsh := t[1] * 100000000H + t[0];
  992. tsh := tsh DIV 10000000;
  993. tsh := tsh - 11644473600;
  994. ts := SHORT(tsh);
  995. second := ts MOD 60;
  996. minute := (ts MOD 3600) DIV 60;
  997. hour := (ts MOD 86400) DIV 3600;
  998. ts := ts - (hour * 3600) - (minute * 60) - second;
  999. totalDays := ts DIV 86400;
  1000. year := 1970;
  1001. continue := TRUE;
  1002. WHILE (totalDays > 365) & continue DO
  1003. IF Dates.LeapYear(year) THEN
  1004. IF totalDays > 366 THEN
  1005. totalDays := totalDays - 366;
  1006. ELSE
  1007. DEC(year);
  1008. continue := FALSE;
  1009. END;
  1010. ELSE
  1011. totalDays := totalDays - 365;
  1012. END;
  1013. INC(year);
  1014. END;
  1015. month := 1;
  1016. continue := TRUE;
  1017. WHILE (totalDays > 28) & continue DO
  1018. NofDaysMnth := Dates.NofDays(year, month);
  1019. IF totalDays >= NofDaysMnth THEN
  1020. INC(month);
  1021. totalDays := totalDays - NofDaysMnth;
  1022. ELSE
  1023. continue := FALSE;
  1024. END;
  1025. END;
  1026. day := totalDays + 1;
  1027. datetime.year := year;
  1028. datetime.month := month;
  1029. datetime.day := day;
  1030. datetime.hour := hour;
  1031. datetime.minute := minute;
  1032. datetime.second := second;
  1033. END GetDateTime;
  1034. END SambaClient.
  1035. System.Free SambaClient ~
  1036. FSTools.Mount SMB SmbFS 192.168.1.1 sharename userid password~
  1037. FSTools.Mount SMB2 SmbFS 127.0.0.1 SBMShared id pwd~
  1038. FSTools.Mount SMB SmbFS 192.168.1.1 ~
  1039. FSTools.Mount SMB SmbFS 192.168.1.99 test ~
  1040. FSTools.Mount SMB SmbFS 192.168.1.99 d ~
  1041. FSTools.Mount SMB SmbFS 129.132.50.25 test guest guest ~
  1042. FSTools.Mount SMB SmbFS 129.132.50.7 test ~
  1043. FSTools.Mount SMB SmbFS 192.168.1.102 test ~
  1044. FSTools.Mount SMB SmbFS 127.0.0.1 ~
  1045. FSTools.Mount SMB SmbFS 127.0.0.1 ~
  1046. FSTools.Unmount SMB ~