* filenotify.el (file-notify--library): Renamed from
[bpt/emacs.git] / lisp / net / tramp-smb.el
1 ;;; tramp-smb.el --- Tramp access functions for SMB servers
2
3 ;; Copyright (C) 2002-2013 Free Software Foundation, Inc.
4
5 ;; Author: Michael Albinus <michael.albinus@gmx.de>
6 ;; Keywords: comm, processes
7 ;; Package: tramp
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;; Access functions for SMB servers like SAMBA or M$ Windows from Tramp.
27
28 ;;; Code:
29
30 (eval-when-compile (require 'cl)) ; block, return
31 (require 'tramp)
32
33 ;; Define SMB method ...
34 ;;;###tramp-autoload
35 (defconst tramp-smb-method "smb"
36 "Method to connect SAMBA and M$ SMB servers.")
37
38 ;; ... and add it to the method list.
39 ;;;###tramp-autoload
40 (unless (memq system-type '(cygwin windows-nt))
41 (add-to-list 'tramp-methods
42 `(,tramp-smb-method
43 ;; We define an empty command, because `tramp-smb-call-winexe'
44 ;; opens already the powershell. Used in `tramp-handle-shell-command'.
45 (tramp-remote-shell "")
46 ;; This is just a guess. We don't know whether the share "C$"
47 ;; is available for public use, and whether the user has write
48 ;; access.
49 (tramp-tmpdir "/C$/Temp"))))
50
51 ;; Add a default for `tramp-default-method-alist'. Rule: If there is
52 ;; a domain in USER, it must be the SMB method.
53 ;;;###tramp-autoload
54 (add-to-list 'tramp-default-method-alist
55 `(nil ,tramp-prefix-domain-regexp ,tramp-smb-method))
56
57 ;; Add a default for `tramp-default-user-alist'. Rule: For the SMB method,
58 ;; the anonymous user is chosen.
59 ;;;###tramp-autoload
60 (add-to-list 'tramp-default-user-alist
61 `(,(concat "\\`" tramp-smb-method "\\'") nil nil))
62
63 ;; Add completion function for SMB method.
64 ;;;###tramp-autoload
65 (eval-after-load 'tramp
66 '(tramp-set-completion-function
67 tramp-smb-method
68 '((tramp-parse-netrc "~/.netrc"))))
69
70 (defcustom tramp-smb-program "smbclient"
71 "Name of SMB client to run."
72 :group 'tramp
73 :type 'string)
74
75 (defcustom tramp-smb-conf "/dev/null"
76 "Path of the smb.conf file.
77 If it is nil, no smb.conf will be added to the `tramp-smb-program'
78 call, letting the SMB client use the default one."
79 :group 'tramp
80 :type '(choice (const nil) (file :must-match t)))
81
82 (defvar tramp-smb-version nil
83 "Version string of the SMB client.")
84
85 (defconst tramp-smb-server-version
86 "Domain=\\[[^]]*\\] OS=\\[[^]]*\\] Server=\\[[^]]*\\]"
87 "Regexp of SMB server identification.")
88
89 (defconst tramp-smb-prompt "^\\(smb:\\|PS\\) .+> \\|^\\s-+Server\\s-+Comment$"
90 "Regexp used as prompt in smbclient or powershell.")
91
92 (defconst tramp-smb-wrong-passwd-regexp
93 (regexp-opt
94 '("NT_STATUS_LOGON_FAILURE"
95 "NT_STATUS_WRONG_PASSWORD"))
96 "Regexp for login error strings of SMB servers.")
97
98 (defconst tramp-smb-errors
99 (mapconcat
100 'identity
101 `(;; Connection error / timeout / unknown command.
102 "Connection\\( to \\S-+\\)? failed"
103 "Read from server failed, maybe it closed the connection"
104 "Call timed out: server did not respond"
105 "\\S-+: command not found"
106 "Server doesn't support UNIX CIFS calls"
107 ,(regexp-opt
108 '(;; Samba.
109 "ERRDOS"
110 "ERRHRD"
111 "ERRSRV"
112 "ERRbadfile"
113 "ERRbadpw"
114 "ERRfilexists"
115 "ERRnoaccess"
116 "ERRnomem"
117 "ERRnosuchshare"
118 ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000),
119 ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003),
120 ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7).
121 "NT_STATUS_ACCESS_DENIED"
122 "NT_STATUS_ACCOUNT_LOCKED_OUT"
123 "NT_STATUS_BAD_NETWORK_NAME"
124 "NT_STATUS_CANNOT_DELETE"
125 "NT_STATUS_CONNECTION_REFUSED"
126 "NT_STATUS_DIRECTORY_NOT_EMPTY"
127 "NT_STATUS_DUPLICATE_NAME"
128 "NT_STATUS_FILE_IS_A_DIRECTORY"
129 "NT_STATUS_IMAGE_ALREADY_LOADED"
130 "NT_STATUS_IO_TIMEOUT"
131 "NT_STATUS_LOGON_FAILURE"
132 "NT_STATUS_NETWORK_ACCESS_DENIED"
133 "NT_STATUS_NOT_IMPLEMENTED"
134 "NT_STATUS_NO_SUCH_FILE"
135 "NT_STATUS_NO_SUCH_USER"
136 "NT_STATUS_OBJECT_NAME_COLLISION"
137 "NT_STATUS_OBJECT_NAME_INVALID"
138 "NT_STATUS_OBJECT_NAME_NOT_FOUND"
139 "NT_STATUS_SHARING_VIOLATION"
140 "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE"
141 "NT_STATUS_UNSUCCESSFUL"
142 "NT_STATUS_WRONG_PASSWORD")))
143 "\\|")
144 "Regexp for possible error strings of SMB servers.
145 Used instead of analyzing error codes of commands.")
146
147 (defconst tramp-smb-actions-with-share
148 '((tramp-smb-prompt tramp-action-succeed)
149 (tramp-password-prompt-regexp tramp-action-password)
150 (tramp-wrong-passwd-regexp tramp-action-permission-denied)
151 (tramp-smb-errors tramp-action-permission-denied)
152 (tramp-process-alive-regexp tramp-action-process-alive))
153 "List of pattern/action pairs.
154 This list is used for login to SMB servers.
155
156 See `tramp-actions-before-shell' for more info.")
157
158 (defconst tramp-smb-actions-without-share
159 '((tramp-password-prompt-regexp tramp-action-password)
160 (tramp-wrong-passwd-regexp tramp-action-permission-denied)
161 (tramp-smb-errors tramp-action-permission-denied)
162 (tramp-process-alive-regexp tramp-action-out-of-band))
163 "List of pattern/action pairs.
164 This list is used for login to SMB servers.
165
166 See `tramp-actions-before-shell' for more info.")
167
168 (defconst tramp-smb-actions-with-tar
169 '((tramp-password-prompt-regexp tramp-action-password)
170 (tramp-wrong-passwd-regexp tramp-action-permission-denied)
171 (tramp-smb-errors tramp-action-permission-denied)
172 (tramp-process-alive-regexp tramp-smb-action-with-tar))
173 "List of pattern/action pairs.
174 This list is used for tar-like copy of directories.
175
176 See `tramp-actions-before-shell' for more info.")
177
178 ;; New handlers should be added here.
179 (defconst tramp-smb-file-name-handler-alist
180 '(
181 ;; `access-file' performed by default handler.
182 (add-name-to-file . tramp-smb-handle-add-name-to-file)
183 ;; `byte-compiler-base-file-name' performed by default handler.
184 (copy-directory . tramp-smb-handle-copy-directory)
185 (copy-file . tramp-smb-handle-copy-file)
186 (delete-directory . tramp-smb-handle-delete-directory)
187 (delete-file . tramp-smb-handle-delete-file)
188 ;; `diff-latest-backup-file' performed by default handler.
189 (directory-file-name . tramp-handle-directory-file-name)
190 (directory-files . tramp-smb-handle-directory-files)
191 (directory-files-and-attributes
192 . tramp-handle-directory-files-and-attributes)
193 (dired-call-process . ignore)
194 (dired-compress-file . ignore)
195 (dired-uncache . tramp-handle-dired-uncache)
196 (expand-file-name . tramp-smb-handle-expand-file-name)
197 (file-accessible-directory-p . tramp-smb-handle-file-directory-p)
198 (file-acl . tramp-smb-handle-file-acl)
199 (file-attributes . tramp-smb-handle-file-attributes)
200 (file-directory-p . tramp-smb-handle-file-directory-p)
201 (file-executable-p . tramp-handle-file-exists-p)
202 (file-exists-p . tramp-handle-file-exists-p)
203 (file-local-copy . tramp-smb-handle-file-local-copy)
204 (file-modes . tramp-handle-file-modes)
205 (file-name-all-completions . tramp-smb-handle-file-name-all-completions)
206 (file-name-as-directory . tramp-handle-file-name-as-directory)
207 (file-name-completion . tramp-handle-file-name-completion)
208 (file-name-directory . tramp-handle-file-name-directory)
209 (file-name-nondirectory . tramp-handle-file-name-nondirectory)
210 ;; `file-name-sans-versions' performed by default handler.
211 (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
212 (file-notify-add-watch . ignore)
213 (file-notify-rm-watch . ignore)
214 (file-notify-supported-p . ignore)
215 (file-ownership-preserved-p . ignore)
216 (file-readable-p . tramp-handle-file-exists-p)
217 (file-regular-p . tramp-handle-file-regular-p)
218 (file-remote-p . tramp-handle-file-remote-p)
219 ;; `file-selinux-context' performed by default handler.
220 (file-symlink-p . tramp-handle-file-symlink-p)
221 ;; `file-truename' performed by default handler.
222 (file-writable-p . tramp-smb-handle-file-writable-p)
223 (find-backup-file-name . tramp-handle-find-backup-file-name)
224 ;; `find-file-noselect' performed by default handler.
225 ;; `get-file-buffer' performed by default handler.
226 (insert-directory . tramp-smb-handle-insert-directory)
227 (insert-file-contents . tramp-handle-insert-file-contents)
228 (load . tramp-handle-load)
229 (make-directory . tramp-smb-handle-make-directory)
230 (make-directory-internal . tramp-smb-handle-make-directory-internal)
231 (make-symbolic-link . tramp-smb-handle-make-symbolic-link)
232 (process-file . tramp-smb-handle-process-file)
233 (rename-file . tramp-smb-handle-rename-file)
234 (set-file-acl . ignore)
235 (set-file-modes . tramp-smb-handle-set-file-modes)
236 (set-file-selinux-context . ignore)
237 (set-file-times . ignore)
238 (set-visited-file-modtime . ignore)
239 (shell-command . tramp-handle-shell-command)
240 (start-file-process . tramp-smb-handle-start-file-process)
241 (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name)
242 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
243 (vc-registered . ignore)
244 (verify-visited-file-modtime . ignore)
245 (write-region . tramp-smb-handle-write-region)
246 )
247 "Alist of handler functions for Tramp SMB method.
248 Operations not mentioned here will be handled by the default Emacs primitives.")
249
250 ;; Options for remote processes via winexe.
251 (defcustom tramp-smb-winexe-program "winexe"
252 "Name of winexe client to run.
253 If it isn't found in the local $PATH, the absolute path of winexe
254 shall be given. This is needed for remote processes."
255 :group 'tramp
256 :type 'string
257 :version "24.3")
258
259 (defcustom tramp-smb-winexe-shell-command "powershell.exe"
260 "Shell to be used for processes on remote machines.
261 This must be Powershell V2 compatible."
262 :group 'tramp
263 :type 'string
264 :version "24.3")
265
266 (defcustom tramp-smb-winexe-shell-command-switch "-file -"
267 "Command switch used together with `tramp-smb-winexe-shell-command'.
268 This can be used to disable echo etc."
269 :group 'tramp
270 :type 'string
271 :version "24.3")
272
273 ;; It must be a `defsubst' in order to push the whole code into
274 ;; tramp-loaddefs.el. Otherwise, there would be recursive autoloading.
275 ;;;###tramp-autoload
276 (defsubst tramp-smb-file-name-p (filename)
277 "Check if it's a filename for SMB servers."
278 (string= (tramp-file-name-method (tramp-dissect-file-name filename))
279 tramp-smb-method))
280
281 ;;;###tramp-autoload
282 (defun tramp-smb-file-name-handler (operation &rest args)
283 "Invoke the SMB related OPERATION.
284 First arg specifies the OPERATION, second arg is a list of arguments to
285 pass to the OPERATION."
286 (let ((fn (assoc operation tramp-smb-file-name-handler-alist)))
287 (if fn
288 (save-match-data (apply (cdr fn) args))
289 (tramp-run-real-handler operation args))))
290
291 ;;;###tramp-autoload
292 (unless (memq system-type '(cygwin windows-nt))
293 (add-to-list 'tramp-foreign-file-name-handler-alist
294 (cons 'tramp-smb-file-name-p 'tramp-smb-file-name-handler)))
295
296
297 ;; File name primitives.
298
299 (defun tramp-smb-handle-add-name-to-file
300 (filename newname &optional ok-if-already-exists)
301 "Like `add-name-to-file' for Tramp files."
302 (unless (tramp-equal-remote filename newname)
303 (with-parsed-tramp-file-name
304 (if (tramp-tramp-file-p filename) filename newname) nil
305 (tramp-error
306 v 'file-error
307 "add-name-to-file: %s"
308 "only implemented for same method, same user, same host")))
309 (with-parsed-tramp-file-name filename v1
310 (with-parsed-tramp-file-name newname v2
311 (when (file-directory-p filename)
312 (tramp-error
313 v2 'file-error
314 "add-name-to-file: %s must not be a directory" filename))
315 (when (and (not ok-if-already-exists)
316 (file-exists-p newname)
317 (not (numberp ok-if-already-exists))
318 (y-or-n-p
319 (format
320 "File %s already exists; make it a new name anyway? "
321 newname)))
322 (tramp-error
323 v2 'file-error
324 "add-name-to-file: file %s already exists" newname))
325 ;; We must also flush the cache of the directory, because
326 ;; `file-attributes' reads the values from there.
327 (tramp-flush-file-property v2 (file-name-directory v2-localname))
328 (tramp-flush-file-property v2 v2-localname)
329 (unless
330 (tramp-smb-send-command
331 v1
332 (format
333 "%s \"%s\" \"%s\""
334 (if (tramp-smb-get-cifs-capabilities v1) "link" "hardlink")
335 (tramp-smb-get-localname v1)
336 (tramp-smb-get-localname v2)))
337 (tramp-error
338 v2 'file-error
339 "error with add-name-to-file, see buffer `%s' for details"
340 (buffer-name))))))
341
342 (defun tramp-smb-action-with-tar (proc vec)
343 "Untar from connection buffer."
344 (if (not (memq (process-status proc) '(run open)))
345 (throw 'tramp-action 'process-died)
346
347 (with-current-buffer (tramp-get-connection-buffer vec)
348 (goto-char (point-min))
349 (when (search-forward-regexp tramp-smb-server-version nil t)
350 ;; There might be a hidden password prompt.
351 (widen)
352 (forward-line)
353 (tramp-message vec 6 (buffer-substring (point-min) (point)))
354 (delete-region (point-min) (point))
355 (throw 'tramp-action 'ok)))))
356
357 (defun tramp-smb-handle-copy-directory
358 (dirname newname &optional keep-date parents copy-contents)
359 "Like `copy-directory' for Tramp files."
360 (setq dirname (expand-file-name dirname)
361 newname (expand-file-name newname))
362 (let ((t1 (tramp-tramp-file-p dirname))
363 (t2 (tramp-tramp-file-p newname)))
364 (with-parsed-tramp-file-name (if t1 dirname newname) nil
365 (with-tramp-progress-reporter
366 v 0 (format "Copying %s to %s" dirname newname)
367 (cond
368 ;; We must use a local temporary directory.
369 ((and t1 t2)
370 (let ((tmpdir
371 (make-temp-name
372 (expand-file-name
373 tramp-temp-name-prefix
374 (tramp-compat-temporary-file-directory)))))
375 (unwind-protect
376 (progn
377 (tramp-compat-copy-directory dirname tmpdir keep-date parents)
378 (tramp-compat-copy-directory tmpdir newname keep-date parents))
379 (tramp-compat-delete-directory tmpdir 'recursive))))
380
381 ;; We can copy recursively.
382 ((or t1 t2)
383 (when (and (file-directory-p newname)
384 (not (string-equal (file-name-nondirectory dirname)
385 (file-name-nondirectory newname))))
386 (setq newname
387 (expand-file-name
388 (file-name-nondirectory dirname) newname))
389 (if t2 (setq v (tramp-dissect-file-name newname))))
390 (if (not (file-directory-p newname))
391 (make-directory newname parents))
392
393 (setq tramp-current-method (tramp-file-name-method v)
394 tramp-current-user (tramp-file-name-user v)
395 tramp-current-host (tramp-file-name-real-host v))
396
397 (let* ((real-user (tramp-file-name-real-user v))
398 (real-host (tramp-file-name-real-host v))
399 (domain (tramp-file-name-domain v))
400 (port (tramp-file-name-port v))
401 (share (tramp-smb-get-share v))
402 (localname (file-name-as-directory
403 (replace-regexp-in-string
404 "\\\\" "/" (tramp-smb-get-localname v))))
405 (tmpdir (make-temp-name
406 (expand-file-name
407 tramp-temp-name-prefix
408 (tramp-compat-temporary-file-directory))))
409 (args (list tramp-smb-program
410 (concat "//" real-host "/" share) "-E")))
411
412 (if (not (zerop (length real-user)))
413 (setq args (append args (list "-U" real-user)))
414 (setq args (append args (list "-N"))))
415
416 (when domain (setq args (append args (list "-W" domain))))
417 (when port (setq args (append args (list "-p" port))))
418 (when tramp-smb-conf
419 (setq args (append args (list "-s" tramp-smb-conf))))
420 (setq args
421 (if t1
422 ;; Source is remote.
423 (append args
424 (list "-D" (shell-quote-argument localname)
425 "-c" (shell-quote-argument "tar qc - *")
426 "|" "tar" "xfC" "-"
427 (shell-quote-argument tmpdir)))
428 ;; Target is remote.
429 (append (list "tar" "cfC" "-" (shell-quote-argument dirname)
430 "." "|")
431 args
432 (list "-D" (shell-quote-argument localname)
433 "-c" (shell-quote-argument "tar qx -")))))
434
435 (unwind-protect
436 (with-temp-buffer
437 ;; Set the transfer process properties.
438 (tramp-set-connection-property
439 v "process-name" (buffer-name (current-buffer)))
440 (tramp-set-connection-property
441 v "process-buffer" (current-buffer))
442
443 (when t1
444 ;; The smbclient tar command creates always complete
445 ;; paths. We must emulate the directory structure,
446 ;; and symlink to the real target.
447 (make-directory
448 (expand-file-name ".." (concat tmpdir localname)) 'parents)
449 (make-symbolic-link
450 newname (directory-file-name (concat tmpdir localname))))
451
452 ;; Use an asynchronous processes. By this, password
453 ;; can be handled.
454 (let* ((default-directory tmpdir)
455 (p (start-process-shell-command
456 (tramp-get-connection-name v)
457 (tramp-get-connection-buffer v)
458 (mapconcat 'identity args " "))))
459
460 (tramp-message
461 v 6 "%s" (mapconcat 'identity (process-command p) " "))
462 (tramp-compat-set-process-query-on-exit-flag p nil)
463 (tramp-process-actions p v nil tramp-smb-actions-with-tar)
464
465 (while (memq (process-status p) '(run open))
466 (sit-for 0.1))
467 (tramp-message v 6 "\n%s" (buffer-string))))
468
469 ;; Reset the transfer process properties.
470 (tramp-set-connection-property v "process-name" nil)
471 (tramp-set-connection-property v "process-buffer" nil)
472 (when t1 (delete-directory tmpdir 'recurse))))
473
474 ;; Handle KEEP-DATE argument.
475 (when keep-date
476 (set-file-times newname (nth 5 (file-attributes dirname))))
477
478 ;; Set the mode.
479 (unless keep-date
480 (set-file-modes newname (tramp-default-file-modes dirname)))
481
482 ;; When newname did exist, we have wrong cached values.
483 (when t2
484 (with-parsed-tramp-file-name newname nil
485 (tramp-flush-file-property v (file-name-directory localname))
486 (tramp-flush-file-property v localname))))
487
488 ;; We must do it file-wise.
489 (t
490 (tramp-run-real-handler
491 'copy-directory (list dirname newname keep-date parents))))))))
492
493 (defun tramp-smb-handle-copy-file
494 (filename newname &optional ok-if-already-exists keep-date
495 preserve-uid-gid preserve-extended-attributes)
496 "Like `copy-file' for Tramp files.
497 KEEP-DATE has no effect in case NEWNAME resides on an SMB server.
498 PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
499 (setq filename (expand-file-name filename)
500 newname (expand-file-name newname))
501 (with-tramp-progress-reporter
502 (tramp-dissect-file-name (if (file-remote-p filename) filename newname))
503 0 (format "Copying %s to %s" filename newname)
504
505 (if (file-directory-p filename)
506 (tramp-compat-copy-directory filename newname keep-date t t)
507
508 (let ((tmpfile (file-local-copy filename)))
509 (if tmpfile
510 ;; Remote filename.
511 (condition-case err
512 (rename-file tmpfile newname ok-if-already-exists)
513 ((error quit)
514 (delete-file tmpfile)
515 (signal (car err) (cdr err))))
516
517 ;; Remote newname.
518 (when (file-directory-p newname)
519 (setq newname
520 (expand-file-name (file-name-nondirectory filename) newname)))
521
522 (with-parsed-tramp-file-name newname nil
523 (when (and (not ok-if-already-exists)
524 (file-exists-p newname))
525 (tramp-error v 'file-already-exists newname))
526
527 ;; We must also flush the cache of the directory, because
528 ;; `file-attributes' reads the values from there.
529 (tramp-flush-file-property v (file-name-directory localname))
530 (tramp-flush-file-property v localname)
531 (unless (tramp-smb-get-share v)
532 (tramp-error
533 v 'file-error "Target `%s' must contain a share name" newname))
534 (unless (tramp-smb-send-command
535 v (format "put \"%s\" \"%s\""
536 filename (tramp-smb-get-localname v)))
537 (tramp-error v 'file-error "Cannot copy `%s'" filename))))))
538
539 ;; KEEP-DATE handling.
540 (when keep-date
541 (set-file-times newname (nth 5 (file-attributes filename))))))
542
543 (defun tramp-smb-handle-delete-directory (directory &optional recursive)
544 "Like `delete-directory' for Tramp files."
545 (setq directory (directory-file-name (expand-file-name directory)))
546 (when (file-exists-p directory)
547 (if recursive
548 (mapc
549 (lambda (file)
550 (if (file-directory-p file)
551 (tramp-compat-delete-directory file recursive)
552 (delete-file file)))
553 ;; We do not want to delete "." and "..".
554 (directory-files
555 directory 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")))
556
557 (with-parsed-tramp-file-name directory nil
558 ;; We must also flush the cache of the directory, because
559 ;; `file-attributes' reads the values from there.
560 (tramp-flush-file-property v (file-name-directory localname))
561 (tramp-flush-directory-property v localname)
562 (unless (tramp-smb-send-command
563 v (format
564 "%s \"%s\""
565 (if (tramp-smb-get-cifs-capabilities v) "posix_rmdir" "rmdir")
566 (tramp-smb-get-localname v)))
567 ;; Error.
568 (with-current-buffer (tramp-get-connection-buffer v)
569 (goto-char (point-min))
570 (search-forward-regexp tramp-smb-errors nil t)
571 (tramp-error
572 v 'file-error "%s `%s'" (match-string 0) directory))))))
573
574 (defun tramp-smb-handle-delete-file (filename &optional trash)
575 "Like `delete-file' for Tramp files."
576 (setq filename (expand-file-name filename))
577 (when (file-exists-p filename)
578 (with-parsed-tramp-file-name filename nil
579 ;; We must also flush the cache of the directory, because
580 ;; `file-attributes' reads the values from there.
581 (tramp-flush-file-property v (file-name-directory localname))
582 (tramp-flush-file-property v localname)
583 (unless (tramp-smb-send-command
584 v (format
585 "%s \"%s\""
586 (if (tramp-smb-get-cifs-capabilities v) "posix_unlink" "rm")
587 (tramp-smb-get-localname v)))
588 ;; Error.
589 (with-current-buffer (tramp-get-connection-buffer v)
590 (goto-char (point-min))
591 (search-forward-regexp tramp-smb-errors nil t)
592 (tramp-error
593 v 'file-error "%s `%s'" (match-string 0) filename))))))
594
595 (defun tramp-smb-handle-directory-files
596 (directory &optional full match nosort)
597 "Like `directory-files' for Tramp files."
598 (let ((result (mapcar 'directory-file-name
599 (file-name-all-completions "" directory))))
600 ;; Discriminate with regexp.
601 (when match
602 (setq result
603 (delete nil
604 (mapcar (lambda (x) (when (string-match match x) x))
605 result))))
606 ;; Append directory.
607 (when full
608 (setq result
609 (mapcar
610 (lambda (x) (expand-file-name x directory))
611 result)))
612 ;; Sort them if necessary.
613 (unless nosort (setq result (sort result 'string-lessp)))
614 ;; That's it.
615 result))
616
617 (defun tramp-smb-handle-expand-file-name (name &optional dir)
618 "Like `expand-file-name' for Tramp files."
619 ;; If DIR is not given, use DEFAULT-DIRECTORY or "/".
620 (setq dir (or dir default-directory "/"))
621 ;; Unless NAME is absolute, concat DIR and NAME.
622 (unless (file-name-absolute-p name)
623 (setq name (concat (file-name-as-directory dir) name)))
624 ;; If NAME is not a Tramp file, run the real handler.
625 (if (not (tramp-tramp-file-p name))
626 (tramp-run-real-handler 'expand-file-name (list name nil))
627 ;; Dissect NAME.
628 (with-parsed-tramp-file-name name nil
629 ;; Tilde expansion if necessary. We use the user name as share,
630 ;; which is often the case in domains.
631 (when (string-match "\\`/?~\\([^/]*\\)" localname)
632 (setq localname
633 (replace-match
634 (if (zerop (length (match-string 1 localname)))
635 (tramp-file-name-real-user v)
636 (match-string 1 localname))
637 nil nil localname)))
638 ;; Make the file name absolute.
639 (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
640 (setq localname (concat "/" localname)))
641 ;; No tilde characters in file name, do normal
642 ;; `expand-file-name' (this does "/./" and "/../").
643 (tramp-make-tramp-file-name
644 method user host
645 (tramp-run-real-handler 'expand-file-name (list localname))))))
646
647 (defun tramp-smb-handle-file-acl (filename)
648 "Like `file-acl' for Tramp files."
649 (with-parsed-tramp-file-name filename nil
650 (with-tramp-file-property v localname "file-acl"
651 (when (tramp-smb-send-command
652 v (format "getfacl \"%s\"" (tramp-smb-get-localname v)))
653 (with-current-buffer (tramp-get-connection-buffer v)
654 (goto-char (point-min))
655 (while (looking-at "^#")
656 (forward-line)
657 (delete-region (point-min) (point)))
658 (goto-char (point-max))
659 (delete-blank-lines)
660 (when (> (point-max) (point-min))
661 (tramp-compat-funcall
662 'substring-no-properties (buffer-string))))))))
663
664 (defun tramp-smb-handle-file-attributes (filename &optional id-format)
665 "Like `file-attributes' for Tramp files."
666 (unless id-format (setq id-format 'integer))
667 (ignore-errors
668 (with-parsed-tramp-file-name filename nil
669 (with-tramp-file-property
670 v localname (format "file-attributes-%s" id-format)
671 (if (and (tramp-smb-get-share v) (tramp-smb-get-stat-capability v))
672 (tramp-smb-do-file-attributes-with-stat v id-format)
673 ;; Reading just the filename entry via "dir localname" is not
674 ;; possible, because when filename is a directory, some
675 ;; smbclient versions return the content of the directory, and
676 ;; other versions don't. Therefore, the whole content of the
677 ;; upper directory is retrieved, and the entry of the filename
678 ;; is extracted from.
679 (let* ((entries (tramp-smb-get-file-entries
680 (file-name-directory filename)))
681 (entry (assoc (file-name-nondirectory filename) entries))
682 (uid (if (equal id-format 'string) "nobody" -1))
683 (gid (if (equal id-format 'string) "nogroup" -1))
684 (inode (tramp-get-inode v))
685 (device (tramp-get-device v)))
686
687 ;; Check result.
688 (when entry
689 (list (and (string-match "d" (nth 1 entry))
690 t) ;0 file type
691 -1 ;1 link count
692 uid ;2 uid
693 gid ;3 gid
694 '(0 0) ;4 atime
695 (nth 3 entry) ;5 mtime
696 '(0 0) ;6 ctime
697 (nth 2 entry) ;7 size
698 (nth 1 entry) ;8 mode
699 nil ;9 gid weird
700 inode ;10 inode number
701 device)))))))) ;11 file system number
702
703 (defun tramp-smb-do-file-attributes-with-stat (vec &optional id-format)
704 "Implement `file-attributes' for Tramp files using stat command."
705 (tramp-message
706 vec 5 "file attributes with stat: %s" (tramp-file-name-localname vec))
707 (with-current-buffer (tramp-get-connection-buffer vec)
708 (let* (size id link uid gid atime mtime ctime mode inode)
709 (when (tramp-smb-send-command
710 vec (format "stat \"%s\"" (tramp-smb-get-localname vec)))
711
712 ;; Loop the listing.
713 (goto-char (point-min))
714 (unless (re-search-forward tramp-smb-errors nil t)
715 (while (not (eobp))
716 (cond
717 ((looking-at
718 "Size:\\s-+\\([0-9]+\\)\\s-+Blocks:\\s-+[0-9]+\\s-+\\(\\w+\\)")
719 (setq size (string-to-number (match-string 1))
720 id (if (string-equal "directory" (match-string 2)) t
721 (if (string-equal "symbolic" (match-string 2)) ""))))
722 ((looking-at
723 "Inode:\\s-+\\([0-9]+\\)\\s-+Links:\\s-+\\([0-9]+\\)")
724 (setq inode (string-to-number (match-string 1))
725 link (string-to-number (match-string 2))))
726 ((looking-at
727 "Access:\\s-+([0-9]+/\\(\\S-+\\))\\s-+Uid:\\s-+\\([0-9]+\\)\\s-+Gid:\\s-+\\([0-9]+\\)")
728 (setq mode (match-string 1)
729 uid (if (equal id-format 'string) (match-string 2)
730 (string-to-number (match-string 2)))
731 gid (if (equal id-format 'string) (match-string 3)
732 (string-to-number (match-string 3)))))
733 ((looking-at
734 "Access:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
735 (setq atime
736 (encode-time
737 (string-to-number (match-string 6)) ;; sec
738 (string-to-number (match-string 5)) ;; min
739 (string-to-number (match-string 4)) ;; hour
740 (string-to-number (match-string 3)) ;; day
741 (string-to-number (match-string 2)) ;; month
742 (string-to-number (match-string 1))))) ;; year
743 ((looking-at
744 "Modify:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
745 (setq mtime
746 (encode-time
747 (string-to-number (match-string 6)) ;; sec
748 (string-to-number (match-string 5)) ;; min
749 (string-to-number (match-string 4)) ;; hour
750 (string-to-number (match-string 3)) ;; day
751 (string-to-number (match-string 2)) ;; month
752 (string-to-number (match-string 1))))) ;; year
753 ((looking-at
754 "Change:\\s-+\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\)\\s-+\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)")
755 (setq ctime
756 (encode-time
757 (string-to-number (match-string 6)) ;; sec
758 (string-to-number (match-string 5)) ;; min
759 (string-to-number (match-string 4)) ;; hour
760 (string-to-number (match-string 3)) ;; day
761 (string-to-number (match-string 2)) ;; month
762 (string-to-number (match-string 1)))))) ;; year
763 (forward-line))
764 ;; Return the result.
765 (list id link uid gid atime mtime ctime size mode nil inode
766 (tramp-get-device vec)))))))
767
768 (defun tramp-smb-handle-file-directory-p (filename)
769 "Like `file-directory-p' for Tramp files."
770 (and (file-exists-p filename)
771 (eq ?d (aref (nth 8 (file-attributes filename)) 0))))
772
773 (defun tramp-smb-handle-file-local-copy (filename)
774 "Like `file-local-copy' for Tramp files."
775 (with-parsed-tramp-file-name filename nil
776 (unless (file-exists-p filename)
777 (tramp-error
778 v 'file-error
779 "Cannot make local copy of non-existing file `%s'" filename))
780 (let ((tmpfile (tramp-compat-make-temp-file filename)))
781 (with-tramp-progress-reporter
782 v 3 (format "Fetching %s to tmp file %s" filename tmpfile)
783 (unless (tramp-smb-send-command
784 v (format "get \"%s\" \"%s\""
785 (tramp-smb-get-localname v) tmpfile))
786 ;; Oops, an error. We shall cleanup.
787 (delete-file tmpfile)
788 (tramp-error
789 v 'file-error "Cannot make local copy of file `%s'" filename)))
790 tmpfile)))
791
792 ;; This function should return "foo/" for directories and "bar" for
793 ;; files.
794 (defun tramp-smb-handle-file-name-all-completions (filename directory)
795 "Like `file-name-all-completions' for Tramp files."
796 (all-completions
797 filename
798 (with-parsed-tramp-file-name directory nil
799 (with-tramp-file-property v localname "file-name-all-completions"
800 (save-match-data
801 (let ((entries (tramp-smb-get-file-entries directory)))
802 (mapcar
803 (lambda (x)
804 (list
805 (if (string-match "d" (nth 1 x))
806 (file-name-as-directory (nth 0 x))
807 (nth 0 x))))
808 entries)))))))
809
810 (defun tramp-smb-handle-file-writable-p (filename)
811 "Like `file-writable-p' for Tramp files."
812 (if (file-exists-p filename)
813 (string-match "w" (or (nth 8 (file-attributes filename)) ""))
814 (let ((dir (file-name-directory filename)))
815 (and (file-exists-p dir)
816 (file-writable-p dir)))))
817
818 (defun tramp-smb-handle-insert-directory
819 (filename switches &optional wildcard full-directory-p)
820 "Like `insert-directory' for Tramp files."
821 (setq filename (expand-file-name filename))
822 (if full-directory-p
823 ;; Called from `dired-add-entry'.
824 (setq filename (file-name-as-directory filename))
825 (setq filename (directory-file-name filename)))
826 (with-parsed-tramp-file-name filename nil
827 (save-match-data
828 (let ((base (file-name-nondirectory filename))
829 ;; We should not destroy the cache entry.
830 (entries (copy-sequence
831 (tramp-smb-get-file-entries
832 (file-name-directory filename)))))
833
834 (when wildcard
835 (string-match "\\." base)
836 (setq base (replace-match "\\\\." nil nil base))
837 (string-match "\\*" base)
838 (setq base (replace-match ".*" nil nil base))
839 (string-match "\\?" base)
840 (setq base (replace-match ".?" nil nil base)))
841
842 ;; Filter entries.
843 (setq entries
844 (delq
845 nil
846 (if (or wildcard (zerop (length base)))
847 ;; Check for matching entries.
848 (mapcar
849 (lambda (x)
850 (when (string-match
851 (format "^%s" base) (nth 0 x))
852 x))
853 entries)
854 ;; We just need the only and only entry FILENAME.
855 (list (assoc base entries)))))
856
857 ;; Sort entries.
858 (setq entries
859 (sort
860 entries
861 (lambda (x y)
862 (if (string-match "t" switches)
863 ;; Sort by date.
864 (tramp-time-less-p (nth 3 y) (nth 3 x))
865 ;; Sort by name.
866 (string-lessp (nth 0 x) (nth 0 y))))))
867
868 ;; Handle "-F" switch.
869 (when (string-match "F" switches)
870 (mapc
871 (lambda (x)
872 (when (not (zerop (length (car x))))
873 (cond
874 ((char-equal ?d (string-to-char (nth 1 x)))
875 (setcar x (concat (car x) "/")))
876 ((char-equal ?x (string-to-char (nth 1 x)))
877 (setcar x (concat (car x) "*"))))))
878 entries))
879
880 ;; Print entries.
881 (mapc
882 (lambda (x)
883 (when (not (zerop (length (nth 0 x))))
884 (let ((attr
885 (when (tramp-smb-get-stat-capability v)
886 (ignore-errors
887 (file-attributes filename 'string)))))
888 (insert
889 (format
890 "%10s %3d %-8s %-8s %8s %s "
891 (or (nth 8 attr) (nth 1 x)) ; mode
892 (or (nth 1 attr) 1) ; inode
893 (or (nth 2 attr) "nobody") ; uid
894 (or (nth 3 attr) "nogroup") ; gid
895 (or (nth 7 attr) (nth 2 x)) ; size
896 (format-time-string
897 (if (tramp-time-less-p
898 (tramp-time-subtract (current-time) (nth 3 x))
899 tramp-half-a-year)
900 "%b %e %R"
901 "%b %e %Y")
902 (nth 3 x)))) ; date
903 ;; We mark the file name. The inserted name could be
904 ;; from somewhere else, so we use the relative file
905 ;; name of `default-directory'.
906 (let ((start (point)))
907 (insert
908 (format
909 "%s\n"
910 (file-relative-name
911 (expand-file-name
912 (nth 0 x) (file-name-directory filename)))))
913 (put-text-property start (1- (point)) 'dired-filename t))
914 (forward-line)
915 (beginning-of-line))))
916 entries)))))
917
918 (defun tramp-smb-handle-make-directory (dir &optional parents)
919 "Like `make-directory' for Tramp files."
920 (setq dir (directory-file-name (expand-file-name dir)))
921 (unless (file-name-absolute-p dir)
922 (setq dir (expand-file-name dir default-directory)))
923 (with-parsed-tramp-file-name dir nil
924 (save-match-data
925 (let* ((ldir (file-name-directory dir)))
926 ;; Make missing directory parts.
927 (when (and parents
928 (tramp-smb-get-share v)
929 (not (file-directory-p ldir)))
930 (make-directory ldir parents))
931 ;; Just do it.
932 (when (file-directory-p ldir)
933 (make-directory-internal dir))
934 (unless (file-directory-p dir)
935 (tramp-error v 'file-error "Couldn't make directory %s" dir))))))
936
937 (defun tramp-smb-handle-make-directory-internal (directory)
938 "Like `make-directory-internal' for Tramp files."
939 (setq directory (directory-file-name (expand-file-name directory)))
940 (unless (file-name-absolute-p directory)
941 (setq directory (expand-file-name directory default-directory)))
942 (with-parsed-tramp-file-name directory nil
943 (save-match-data
944 (let* ((file (tramp-smb-get-localname v)))
945 (when (file-directory-p (file-name-directory directory))
946 (tramp-smb-send-command
947 v
948 (if (tramp-smb-get-cifs-capabilities v)
949 (format
950 "posix_mkdir \"%s\" %s"
951 file (tramp-compat-decimal-to-octal (default-file-modes)))
952 (format "mkdir \"%s\"" file)))
953 ;; We must also flush the cache of the directory, because
954 ;; `file-attributes' reads the values from there.
955 (tramp-flush-file-property v (file-name-directory localname))
956 (tramp-flush-file-property v localname))
957 (unless (file-directory-p directory)
958 (tramp-error
959 v 'file-error "Couldn't make directory %s" directory))))))
960
961 (defun tramp-smb-handle-make-symbolic-link
962 (filename linkname &optional ok-if-already-exists)
963 "Like `make-symbolic-link' for Tramp files.
964 If LINKNAME is a non-Tramp file, it is used verbatim as the target of
965 the symlink. If LINKNAME is a Tramp file, only the localname component is
966 used as the target of the symlink.
967
968 If LINKNAME is a Tramp file and the localname component is relative, then
969 it is expanded first, before the localname component is taken. Note that
970 this can give surprising results if the user/host for the source and
971 target of the symlink differ."
972 (unless (tramp-equal-remote filename linkname)
973 (with-parsed-tramp-file-name
974 (if (tramp-tramp-file-p filename) filename linkname) nil
975 (tramp-error
976 v 'file-error
977 "make-symbolic-link: %s"
978 "only implemented for same method, same user, same host")))
979 (with-parsed-tramp-file-name filename v1
980 (with-parsed-tramp-file-name linkname v2
981 (when (file-directory-p filename)
982 (tramp-error
983 v2 'file-error
984 "make-symbolic-link: %s must not be a directory" filename))
985 (when (and (not ok-if-already-exists)
986 (file-exists-p linkname)
987 (not (numberp ok-if-already-exists))
988 (y-or-n-p
989 (format
990 "File %s already exists; make it a new name anyway? "
991 linkname)))
992 (tramp-error
993 v2 'file-error
994 "make-symbolic-link: file %s already exists" linkname))
995 (unless (tramp-smb-get-cifs-capabilities v1)
996 (tramp-error v2 'file-error "make-symbolic-link not supported"))
997 ;; We must also flush the cache of the directory, because
998 ;; `file-attributes' reads the values from there.
999 (tramp-flush-file-property v2 (file-name-directory v2-localname))
1000 (tramp-flush-file-property v2 v2-localname)
1001 (unless
1002 (tramp-smb-send-command
1003 v1
1004 (format
1005 "symlink \"%s\" \"%s\""
1006 (tramp-smb-get-localname v1)
1007 (tramp-smb-get-localname v2)))
1008 (tramp-error
1009 v2 'file-error
1010 "error with make-symbolic-link, see buffer `%s' for details"
1011 (buffer-name))))))
1012
1013 (defun tramp-smb-handle-process-file
1014 (program &optional infile destination display &rest args)
1015 "Like `process-file' for Tramp files."
1016 ;; The implementation is not complete yet.
1017 (when (and (numberp destination) (zerop destination))
1018 (error "Implementation does not handle immediate return"))
1019
1020 (with-parsed-tramp-file-name default-directory nil
1021 (let* ((name (file-name-nondirectory program))
1022 (name1 name)
1023 (i 0)
1024 input tmpinput outbuf command ret)
1025
1026 ;; Determine input.
1027 (when infile
1028 (setq infile (expand-file-name infile))
1029 (if (tramp-equal-remote default-directory infile)
1030 ;; INFILE is on the same remote host.
1031 (setq input (with-parsed-tramp-file-name infile nil localname))
1032 ;; INFILE must be copied to remote host.
1033 (setq input (tramp-make-tramp-temp-file v)
1034 tmpinput (tramp-make-tramp-file-name method user host input))
1035 (copy-file infile tmpinput t))
1036 ;; Transform input into a filename powershell does understand.
1037 (setq input (format "//%s%s" host input)))
1038
1039 ;; Determine output.
1040 (cond
1041 ;; Just a buffer.
1042 ((bufferp destination)
1043 (setq outbuf destination))
1044 ;; A buffer name.
1045 ((stringp destination)
1046 (setq outbuf (get-buffer-create destination)))
1047 ;; (REAL-DESTINATION ERROR-DESTINATION)
1048 ((consp destination)
1049 ;; output.
1050 (cond
1051 ((bufferp (car destination))
1052 (setq outbuf (car destination)))
1053 ((stringp (car destination))
1054 (setq outbuf (get-buffer-create (car destination))))
1055 ((car destination)
1056 (setq outbuf (current-buffer))))
1057 ;; stderr.
1058 (tramp-message v 2 "%s" "STDERR not supported"))
1059 ;; 't
1060 (destination
1061 (setq outbuf (current-buffer))))
1062
1063 ;; Construct command.
1064 (setq command (mapconcat 'identity (cons program args) " ")
1065 command (if input
1066 (format
1067 "get-content %s | & %s"
1068 (tramp-smb-shell-quote-argument input) command)
1069 (format "& %s" command)))
1070
1071 (while (get-process name1)
1072 ;; NAME must be unique as process name.
1073 (setq i (1+ i)
1074 name1 (format "%s<%d>" name i)))
1075
1076 ;; Set the new process properties.
1077 (tramp-set-connection-property v "process-name" name1)
1078 (tramp-set-connection-property
1079 v "process-buffer"
1080 (or outbuf (generate-new-buffer tramp-temp-buffer-name)))
1081
1082 ;; Call it.
1083 (condition-case nil
1084 (with-current-buffer (tramp-get-connection-buffer v)
1085 ;; Preserve buffer contents.
1086 (narrow-to-region (point-max) (point-max))
1087 (tramp-smb-call-winexe v)
1088 (when (tramp-smb-get-share v)
1089 (tramp-smb-send-command
1090 v (format "cd \"//%s%s\"" host (file-name-directory localname))))
1091 (tramp-smb-send-command v command)
1092 ;; Preserve command output.
1093 (narrow-to-region (point-max) (point-max))
1094 (let ((p (tramp-get-connection-process v)))
1095 (tramp-smb-send-command v "exit $lasterrorcode")
1096 (while (memq (process-status p) '(run open))
1097 (sleep-for 0.1)
1098 (setq ret (process-exit-status p))))
1099 (delete-region (point-min) (point-max))
1100 (widen))
1101
1102 ;; When the user did interrupt, we should do it also. We use
1103 ;; return code -1 as marker.
1104 (quit
1105 (setq ret -1))
1106 ;; Handle errors.
1107 (error
1108 (setq ret 1)))
1109
1110 ;; We should show the output anyway.
1111 (when (and outbuf display) (display-buffer outbuf))
1112
1113 ;; Cleanup. We remove all file cache values for the connection,
1114 ;; because the remote process could have changed them.
1115 (tramp-set-connection-property v "process-name" nil)
1116 (tramp-set-connection-property v "process-buffer" nil)
1117 (when tmpinput (delete-file tmpinput))
1118 (unless outbuf
1119 (kill-buffer (tramp-get-connection-property v "process-buffer" nil)))
1120
1121 ;; `process-file-side-effects' has been introduced with GNU
1122 ;; Emacs 23.2. If set to `nil', no remote file will be changed
1123 ;; by `program'. If it doesn't exist, we assume its default
1124 ;; value `t'.
1125 (unless (and (boundp 'process-file-side-effects)
1126 (not (symbol-value 'process-file-side-effects)))
1127 (tramp-flush-directory-property v ""))
1128
1129 ;; Return exit status.
1130 (if (equal ret -1)
1131 (keyboard-quit)
1132 ret))))
1133
1134 (defun tramp-smb-handle-rename-file
1135 (filename newname &optional ok-if-already-exists)
1136 "Like `rename-file' for Tramp files."
1137 (setq filename (expand-file-name filename)
1138 newname (expand-file-name newname))
1139
1140 (when (and (not ok-if-already-exists)
1141 (file-exists-p newname))
1142 (tramp-error
1143 (tramp-dissect-file-name
1144 (if (file-remote-p filename) filename newname))
1145 'file-already-exists newname))
1146
1147 (with-tramp-progress-reporter
1148 (tramp-dissect-file-name (if (file-remote-p filename) filename newname))
1149 0 (format "Renaming %s to %s" filename newname)
1150
1151 (if (and (tramp-equal-remote filename newname)
1152 (string-equal
1153 (tramp-smb-get-share (tramp-dissect-file-name filename))
1154 (tramp-smb-get-share (tramp-dissect-file-name newname))))
1155 ;; We can rename directly.
1156 (with-parsed-tramp-file-name filename v1
1157 (with-parsed-tramp-file-name newname v2
1158
1159 ;; We must also flush the cache of the directory, because
1160 ;; `file-attributes' reads the values from there.
1161 (tramp-flush-file-property v2 (file-name-directory v2-localname))
1162 (tramp-flush-file-property v2 v2-localname)
1163 (unless (tramp-smb-get-share v2)
1164 (tramp-error
1165 v2 'file-error "Target `%s' must contain a share name" newname))
1166 (unless (tramp-smb-send-command
1167 v2 (format "rename \"%s\" \"%s\""
1168 (tramp-smb-get-localname v1)
1169 (tramp-smb-get-localname v2)))
1170 (tramp-error v2 'file-error "Cannot rename `%s'" filename))))
1171
1172 ;; We must rename via copy.
1173 (tramp-compat-copy-file filename newname ok-if-already-exists t t t)
1174 (if (file-directory-p filename)
1175 (tramp-compat-delete-directory filename 'recursive)
1176 (delete-file filename)))))
1177
1178 (defun tramp-smb-handle-set-file-modes (filename mode)
1179 "Like `set-file-modes' for Tramp files."
1180 (with-parsed-tramp-file-name filename nil
1181 (when (tramp-smb-get-cifs-capabilities v)
1182 (tramp-flush-file-property v localname)
1183 (unless (tramp-smb-send-command
1184 v (format "chmod \"%s\" %s"
1185 (tramp-smb-get-localname v)
1186 (tramp-compat-decimal-to-octal mode)))
1187 (tramp-error
1188 v 'file-error "Error while changing file's mode %s" filename)))))
1189
1190 ;; We use BUFFER also as connection buffer during setup. Because of
1191 ;; this, its original contents must be saved, and restored once
1192 ;; connection has been setup.
1193 (defun tramp-smb-handle-start-file-process (name buffer program &rest args)
1194 "Like `start-file-process' for Tramp files."
1195 (with-parsed-tramp-file-name default-directory nil
1196 (let ((command (mapconcat 'identity (cons program args) " "))
1197 (bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
1198 (name1 name)
1199 (i 0))
1200 (unwind-protect
1201 (save-excursion
1202 (save-restriction
1203 (unless buffer
1204 ;; BUFFER can be nil. We use a temporary buffer.
1205 (setq buffer (generate-new-buffer tramp-temp-buffer-name)))
1206 (while (get-process name1)
1207 ;; NAME must be unique as process name.
1208 (setq i (1+ i)
1209 name1 (format "%s<%d>" name i)))
1210 ;; Set the new process properties.
1211 (tramp-set-connection-property v "process-name" name1)
1212 (tramp-set-connection-property v "process-buffer" buffer)
1213 ;; Activate narrowing in order to save BUFFER contents.
1214 (with-current-buffer (tramp-get-connection-buffer v)
1215 (let ((buffer-undo-list t))
1216 (narrow-to-region (point-max) (point-max))
1217 (tramp-smb-call-winexe v)
1218 (when (tramp-smb-get-share v)
1219 (tramp-smb-send-command
1220 v (format
1221 "cd \"//%s%s\""
1222 host (file-name-directory localname))))
1223 (tramp-message v 6 "(%s); exit" command)
1224 (tramp-send-string v command)))
1225 ;; Return value.
1226 (tramp-get-connection-process v)))
1227
1228 ;; Save exit.
1229 (with-current-buffer (tramp-get-connection-buffer v)
1230 (if (string-match tramp-temp-buffer-name (buffer-name))
1231 (progn
1232 (set-process-buffer (tramp-get-connection-process v) nil)
1233 (kill-buffer (current-buffer)))
1234 (set-buffer-modified-p bmp)))
1235 (tramp-set-connection-property v "process-name" nil)
1236 (tramp-set-connection-property v "process-buffer" nil)))))
1237
1238 (defun tramp-smb-handle-substitute-in-file-name (filename)
1239 "Like `handle-substitute-in-file-name' for Tramp files.
1240 \"//\" substitutes only in the local filename part. Catches
1241 errors for shares like \"C$/\", which are common in Microsoft Windows."
1242 (with-parsed-tramp-file-name filename nil
1243 ;; Ignore in LOCALNAME everything before "//".
1244 (when (and (stringp localname) (string-match ".+?/\\(/\\|~\\)" localname))
1245 (setq filename
1246 (concat (file-remote-p filename)
1247 (replace-match "\\1" nil nil localname)))))
1248 (condition-case nil
1249 (tramp-run-real-handler 'substitute-in-file-name (list filename))
1250 (error filename)))
1251
1252 (defun tramp-smb-handle-write-region
1253 (start end filename &optional append visit lockname confirm)
1254 "Like `write-region' for Tramp files."
1255 (setq filename (expand-file-name filename))
1256 (with-parsed-tramp-file-name filename nil
1257 (unless (eq append nil)
1258 (tramp-error
1259 v 'file-error "Cannot append to file using Tramp (`%s')" filename))
1260 ;; XEmacs takes a coding system as the seventh argument, not `confirm'.
1261 (when (and (not (featurep 'xemacs))
1262 confirm (file-exists-p filename))
1263 (unless (y-or-n-p (format "File %s exists; overwrite anyway? "
1264 filename))
1265 (tramp-error v 'file-error "File not overwritten")))
1266 ;; We must also flush the cache of the directory, because
1267 ;; `file-attributes' reads the values from there.
1268 (tramp-flush-file-property v (file-name-directory localname))
1269 (tramp-flush-file-property v localname)
1270 (let ((curbuf (current-buffer))
1271 (tmpfile (tramp-compat-make-temp-file filename)))
1272 ;; We say `no-message' here because we don't want the visited file
1273 ;; modtime data to be clobbered from the temp file. We call
1274 ;; `set-visited-file-modtime' ourselves later on.
1275 (tramp-run-real-handler
1276 'write-region
1277 (if confirm ; don't pass this arg unless defined for backward compat.
1278 (list start end tmpfile append 'no-message lockname confirm)
1279 (list start end tmpfile append 'no-message lockname)))
1280
1281 (with-tramp-progress-reporter
1282 v 3 (format "Moving tmp file %s to %s" tmpfile filename)
1283 (unwind-protect
1284 (unless (tramp-smb-send-command
1285 v (format "put %s \"%s\""
1286 tmpfile (tramp-smb-get-localname v)))
1287 (tramp-error v 'file-error "Cannot write `%s'" filename))
1288 (delete-file tmpfile)))
1289
1290 (unless (equal curbuf (current-buffer))
1291 (tramp-error
1292 v 'file-error
1293 "Buffer has changed from `%s' to `%s'" curbuf (current-buffer)))
1294 (when (eq visit t)
1295 (set-visited-file-modtime)))))
1296
1297
1298 ;; Internal file name functions.
1299
1300 (defun tramp-smb-get-share (vec)
1301 "Returns the share name of LOCALNAME."
1302 (save-match-data
1303 (let ((localname (tramp-file-name-localname vec)))
1304 (when (string-match "^/?\\([^/]+\\)/" localname)
1305 (match-string 1 localname)))))
1306
1307 (defun tramp-smb-get-localname (vec)
1308 "Returns the file name of LOCALNAME.
1309 If VEC has no cifs capabilities, exchange \"/\" by \"\\\\\"."
1310 (save-match-data
1311 (let ((localname (tramp-file-name-localname vec)))
1312 (setq
1313 localname
1314 (if (string-match "^/?[^/]+\\(/.*\\)" localname)
1315 ;; There is a share, separated by "/".
1316 (if (not (tramp-smb-get-cifs-capabilities vec))
1317 (mapconcat
1318 (lambda (x) (if (equal x ?/) "\\" (char-to-string x)))
1319 (match-string 1 localname) "")
1320 (match-string 1 localname))
1321 ;; There is just a share.
1322 (if (string-match "^/?\\([^/]+\\)$" localname)
1323 (match-string 1 localname)
1324 "")))
1325
1326 ;; Sometimes we have discarded `substitute-in-file-name'.
1327 (when (string-match "\\(\\$\\$\\)\\(/\\|$\\)" localname)
1328 (setq localname (replace-match "$" nil nil localname 1)))
1329
1330 localname)))
1331
1332 ;; Share names of a host are cached. It is very unlikely that the
1333 ;; shares do change during connection.
1334 (defun tramp-smb-get-file-entries (directory)
1335 "Read entries which match DIRECTORY.
1336 Either the shares are listed, or the `dir' command is executed.
1337 Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)."
1338 (with-parsed-tramp-file-name (file-name-as-directory directory) nil
1339 (setq localname (or localname "/"))
1340 (with-tramp-file-property v localname "file-entries"
1341 (with-current-buffer (tramp-get-connection-buffer v)
1342 (let* ((share (tramp-smb-get-share v))
1343 (cache (tramp-get-connection-property v "share-cache" nil))
1344 res entry)
1345
1346 (if (and (not share) cache)
1347 ;; Return cached shares.
1348 (setq res cache)
1349
1350 ;; Read entries.
1351 (if share
1352 (tramp-smb-send-command
1353 v (format "dir \"%s*\"" (tramp-smb-get-localname v)))
1354 ;; `tramp-smb-maybe-open-connection' lists also the share names.
1355 (tramp-smb-maybe-open-connection v))
1356
1357 ;; Loop the listing.
1358 (goto-char (point-min))
1359 (if (re-search-forward tramp-smb-errors nil t)
1360 (tramp-error v 'file-error "%s `%s'" (match-string 0) directory)
1361 (while (not (eobp))
1362 (setq entry (tramp-smb-read-file-entry share))
1363 (forward-line)
1364 (when entry (add-to-list 'res entry))))
1365
1366 ;; Cache share entries.
1367 (unless share
1368 (tramp-set-connection-property v "share-cache" res)))
1369
1370 ;; Add directory itself.
1371 (add-to-list 'res '("" "drwxrwxrwx" 0 (0 0)))
1372
1373 ;; There's a very strange error (debugged with XEmacs 21.4.14)
1374 ;; If there's no short delay, it returns nil. No idea about.
1375 (when (featurep 'xemacs) (sleep-for 0.01))
1376
1377 ;; Return entries.
1378 (delq nil res))))))
1379
1380 ;; Return either a share name (if SHARE is nil), or a file name.
1381 ;;
1382 ;; If shares are listed, the following format is expected:
1383 ;;
1384 ;; Disk| - leading spaces
1385 ;; [^|]+| - share name, 14 char
1386 ;; .* - comment
1387 ;;
1388 ;; Entries provided by smbclient DIR aren't fully regular.
1389 ;; They should have the format
1390 ;;
1391 ;; \s-\{2,2} - leading spaces
1392 ;; \S-\(.*\S-\)\s-* - file name, 30 chars, left bound
1393 ;; \s-+[ADHRSV]* - permissions, 7 chars, right bound
1394 ;; \s- - space delimiter
1395 ;; \s-+[0-9]+ - size, 8 chars, right bound
1396 ;; \s-\{2,2\} - space delimiter
1397 ;; \w\{3,3\} - weekday
1398 ;; \s- - space delimiter
1399 ;; \w\{3,3\} - month
1400 ;; \s- - space delimiter
1401 ;; [ 12][0-9] - day
1402 ;; \s- - space delimiter
1403 ;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time
1404 ;; \s- - space delimiter
1405 ;; [0-9]\{4,4\} - year
1406 ;;
1407 ;; samba/src/client.c (http://samba.org/doxygen/samba/client_8c-source.html)
1408 ;; has function display_finfo:
1409 ;;
1410 ;; d_printf(" %-30s%7.7s %8.0f %s",
1411 ;; finfo->name,
1412 ;; attrib_string(finfo->mode),
1413 ;; (double)finfo->size,
1414 ;; asctime(LocalTime(&t)));
1415 ;;
1416 ;; in Samba 1.9, there's the following code:
1417 ;;
1418 ;; DEBUG(0,(" %-30s%7.7s%10d %s",
1419 ;; CNV_LANG(finfo->name),
1420 ;; attrib_string(finfo->mode),
1421 ;; finfo->size,
1422 ;; asctime(LocalTime(&t))));
1423 ;;
1424 ;; Problems:
1425 ;; * Modern regexp constructs, like spy groups and counted repetitions, aren't
1426 ;; available in older Emacsen.
1427 ;; * The length of constructs (file name, size) might exceed the default.
1428 ;; * File names might contain spaces.
1429 ;; * Permissions might be empty.
1430 ;;
1431 ;; So we try to analyze backwards.
1432 (defun tramp-smb-read-file-entry (share)
1433 "Parse entry in SMB output buffer.
1434 If SHARE is result, entries are of type dir. Otherwise, shares are listed.
1435 Result is the list (LOCALNAME MODE SIZE MTIME)."
1436 ;; We are called from `tramp-smb-get-file-entries', which sets the
1437 ;; current buffer.
1438 (let ((line (buffer-substring (point) (point-at-eol)))
1439 localname mode size month day hour min sec year mtime)
1440
1441 (if (not share)
1442
1443 ;; Read share entries.
1444 (when (string-match "^Disk|\\([^|]+\\)|" line)
1445 (setq localname (match-string 1 line)
1446 mode "dr-xr-xr-x"
1447 size 0))
1448
1449 ;; Real listing.
1450 (block nil
1451
1452 ;; year.
1453 (if (string-match "\\([0-9]+\\)$" line)
1454 (setq year (string-to-number (match-string 1 line))
1455 line (substring line 0 -5))
1456 (return))
1457
1458 ;; time.
1459 (if (string-match "\\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\)$" line)
1460 (setq hour (string-to-number (match-string 1 line))
1461 min (string-to-number (match-string 2 line))
1462 sec (string-to-number (match-string 3 line))
1463 line (substring line 0 -9))
1464 (return))
1465
1466 ;; day.
1467 (if (string-match "\\([0-9]+\\)$" line)
1468 (setq day (string-to-number (match-string 1 line))
1469 line (substring line 0 -3))
1470 (return))
1471
1472 ;; month.
1473 (if (string-match "\\(\\w+\\)$" line)
1474 (setq month (match-string 1 line)
1475 line (substring line 0 -4))
1476 (return))
1477
1478 ;; weekday.
1479 (if (string-match "\\(\\w+\\)$" line)
1480 (setq line (substring line 0 -5))
1481 (return))
1482
1483 ;; size.
1484 (if (string-match "\\([0-9]+\\)$" line)
1485 (let ((length (- (max 10 (1+ (length (match-string 1 line)))))))
1486 (setq size (string-to-number (match-string 1 line)))
1487 (when (string-match "\\([ADHRSV]+\\)" (substring line length))
1488 (setq length (+ length (match-end 0))))
1489 (setq line (substring line 0 length)))
1490 (return))
1491
1492 ;; mode: ARCH, DIR, HIDDEN, RONLY, SYSTEM, VOLID.
1493 (if (string-match "\\([ADHRSV]+\\)?$" line)
1494 (setq
1495 mode (or (match-string 1 line) "")
1496 mode (save-match-data (format
1497 "%s%s"
1498 (if (string-match "D" mode) "d" "-")
1499 (mapconcat
1500 (lambda (x) "") " "
1501 (concat "r" (if (string-match "R" mode) "-" "w") "x"))))
1502 line (substring line 0 -6))
1503 (return))
1504
1505 ;; localname.
1506 (if (string-match "^\\s-+\\(\\S-\\(.*\\S-\\)?\\)\\s-*$" line)
1507 (setq localname (match-string 1 line))
1508 (return))))
1509
1510 (when (and localname mode size)
1511 (setq mtime
1512 (if (and sec min hour day month year)
1513 (encode-time
1514 sec min hour day
1515 (cdr (assoc (downcase month) tramp-parse-time-months))
1516 year)
1517 '(0 0)))
1518 (list localname mode size mtime))))
1519
1520 (defun tramp-smb-get-cifs-capabilities (vec)
1521 "Check, whether the SMB server supports POSIX commands."
1522 ;; When we are not logged in yet, we return nil.
1523 (if (let ((p (tramp-get-connection-process vec)))
1524 (and p (processp p) (memq (process-status p) '(run open))))
1525 (with-tramp-connection-property
1526 (tramp-get-connection-process vec) "cifs-capabilities"
1527 (save-match-data
1528 (when (tramp-smb-send-command vec "posix")
1529 (with-current-buffer (tramp-get-connection-buffer vec)
1530 (goto-char (point-min))
1531 (when
1532 (re-search-forward "Server supports CIFS capabilities" nil t)
1533 (member
1534 "pathnames"
1535 (split-string
1536 (buffer-substring (point) (point-at-eol)) nil t)))))))))
1537
1538 (defun tramp-smb-get-stat-capability (vec)
1539 "Check, whether the SMB server supports the STAT command."
1540 ;; When we are not logged in yet, we return nil.
1541 (if (let ((p (tramp-get-connection-process vec)))
1542 (and p (processp p) (memq (process-status p) '(run open))))
1543 (with-tramp-connection-property
1544 (tramp-get-connection-process vec) "stat-capability"
1545 (tramp-smb-send-command vec "stat ."))))
1546
1547
1548 ;; Connection functions.
1549
1550 (defun tramp-smb-send-command (vec command)
1551 "Send the COMMAND to connection VEC.
1552 Returns nil if there has been an error message from smbclient."
1553 (tramp-smb-maybe-open-connection vec)
1554 (tramp-message vec 6 "%s" command)
1555 (tramp-send-string vec command)
1556 (tramp-smb-wait-for-output vec))
1557
1558 (defun tramp-smb-maybe-open-connection (vec &optional argument)
1559 "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'.
1560 Does not do anything if a connection is already open, but re-opens the
1561 connection if a previous connection has died for some reason.
1562 If ARGUMENT is non-nil, use it as argument for
1563 `tramp-smb-winexe-program', and suppress any checks."
1564 (let* ((share (tramp-smb-get-share vec))
1565 (buf (tramp-get-connection-buffer vec))
1566 (p (get-buffer-process buf)))
1567
1568 ;; Check whether we still have the same smbclient version.
1569 ;; Otherwise, we must delete the connection cache, because
1570 ;; capabilities migh have changed.
1571 (unless (or argument (processp p))
1572 (let ((default-directory (tramp-compat-temporary-file-directory))
1573 (command (concat tramp-smb-program " -V")))
1574
1575 (unless tramp-smb-version
1576 (unless (executable-find tramp-smb-program)
1577 (tramp-error
1578 vec 'file-error
1579 "Cannot find command %s in %s" tramp-smb-program exec-path))
1580 (setq tramp-smb-version (shell-command-to-string command))
1581 (tramp-message vec 6 command)
1582 (tramp-message vec 6 "\n%s" tramp-smb-version)
1583 (if (string-match "[ \t\n\r]+\\'" tramp-smb-version)
1584 (setq tramp-smb-version
1585 (replace-match "" nil nil tramp-smb-version))))
1586
1587 (unless (string-equal
1588 tramp-smb-version
1589 (tramp-get-connection-property
1590 vec "smbclient-version" tramp-smb-version))
1591 (tramp-flush-directory-property vec "")
1592 (tramp-flush-connection-property vec))
1593
1594 (tramp-set-connection-property
1595 vec "smbclient-version" tramp-smb-version)))
1596
1597 ;; If too much time has passed since last command was sent, look
1598 ;; whether there has been an error message; maybe due to
1599 ;; connection timeout.
1600 (with-current-buffer buf
1601 (goto-char (point-min))
1602 (when (and (> (tramp-time-diff
1603 (current-time)
1604 (tramp-get-connection-property
1605 p "last-cmd-time" '(0 0 0)))
1606 60)
1607 p (processp p) (memq (process-status p) '(run open))
1608 (re-search-forward tramp-smb-errors nil t))
1609 (delete-process p)
1610 (setq p nil)))
1611
1612 ;; Check whether it is still the same share.
1613 (unless
1614 (and p (processp p) (memq (process-status p) '(run open))
1615 (or argument
1616 (string-equal
1617 share
1618 (tramp-get-connection-property p "smb-share" ""))))
1619
1620 (save-match-data
1621 ;; There might be unread output from checking for share names.
1622 (when buf (with-current-buffer buf (erase-buffer)))
1623 (when (and p (processp p)) (delete-process p))
1624
1625 (let* ((user (tramp-file-name-user vec))
1626 (host (tramp-file-name-host vec))
1627 (real-user (tramp-file-name-real-user vec))
1628 (real-host (tramp-file-name-real-host vec))
1629 (domain (tramp-file-name-domain vec))
1630 (port (tramp-file-name-port vec))
1631 args)
1632
1633 (cond
1634 (argument
1635 (setq args (list (concat "//" real-host))))
1636 (share
1637 (setq args (list (concat "//" real-host "/" share))))
1638 (t
1639 (setq args (list "-g" "-L" real-host ))))
1640
1641 (if (not (zerop (length real-user)))
1642 (setq args (append args (list "-U" real-user)))
1643 (setq args (append args (list "-N"))))
1644
1645 (when domain (setq args (append args (list "-W" domain))))
1646 (when port (setq args (append args (list "-p" port))))
1647 (when tramp-smb-conf
1648 (setq args (append args (list "-s" tramp-smb-conf))))
1649 (when argument
1650 (setq args (append args (list argument))))
1651
1652 ;; OK, let's go.
1653 (with-tramp-progress-reporter
1654 vec 3
1655 (format "Opening connection for //%s%s/%s"
1656 (if (not (zerop (length user))) (concat user "@") "")
1657 host (or share ""))
1658
1659 (let* ((coding-system-for-read nil)
1660 (process-connection-type tramp-process-connection-type)
1661 (p (let ((default-directory
1662 (tramp-compat-temporary-file-directory)))
1663 (apply #'start-process
1664 (tramp-get-connection-name vec)
1665 (tramp-get-connection-buffer vec)
1666 (if argument
1667 tramp-smb-winexe-program tramp-smb-program)
1668 args))))
1669
1670 (tramp-message
1671 vec 6 "%s" (mapconcat 'identity (process-command p) " "))
1672 (tramp-compat-set-process-query-on-exit-flag p nil)
1673
1674 ;; Set variables for computing the prompt for reading password.
1675 (setq tramp-current-method tramp-smb-method
1676 tramp-current-user user
1677 tramp-current-host host)
1678
1679 (condition-case err
1680 (let (tramp-message-show-message)
1681 ;; Play login scenario.
1682 (tramp-process-actions
1683 p vec nil
1684 (if (or argument share)
1685 tramp-smb-actions-with-share
1686 tramp-smb-actions-without-share))
1687
1688 ;; Check server version.
1689 (unless argument
1690 (with-current-buffer (tramp-get-connection-buffer vec)
1691 (goto-char (point-min))
1692 (search-forward-regexp tramp-smb-server-version nil t)
1693 (let ((smbserver-version (match-string 0)))
1694 (unless
1695 (string-equal
1696 smbserver-version
1697 (tramp-get-connection-property
1698 vec "smbserver-version" smbserver-version))
1699 (tramp-flush-directory-property vec "")
1700 (tramp-flush-connection-property vec))
1701 (tramp-set-connection-property
1702 vec "smbserver-version" smbserver-version))))
1703
1704 ;; Set chunksize to 1. smbclient reads its input
1705 ;; character by character; if we send the string
1706 ;; at once, it is read painfully slow.
1707 (tramp-set-connection-property p "smb-share" share)
1708 (tramp-set-connection-property p "chunksize" 1))
1709
1710 ;; Check for the error reason. If it was due to wrong
1711 ;; password, reestablish the connection. We cannot
1712 ;; handle this in `tramp-process-actions', because
1713 ;; smbclient does not ask for the password, again.
1714 (error
1715 (with-current-buffer (tramp-get-connection-buffer vec)
1716 (goto-char (point-min))
1717 (if (search-forward-regexp
1718 tramp-smb-wrong-passwd-regexp nil t)
1719 ;; Disable `auth-source' and `password-cache'.
1720 (let (auth-sources)
1721 (tramp-cleanup vec)
1722 (tramp-smb-maybe-open-connection vec argument))
1723 ;; Propagate the error.
1724 (signal (car err) (cdr err)))))))))))))
1725
1726 ;; We don't use timeouts. If needed, the caller shall wrap around.
1727 (defun tramp-smb-wait-for-output (vec)
1728 "Wait for output from smbclient command.
1729 Returns nil if an error message has appeared."
1730 (with-current-buffer (tramp-get-connection-buffer vec)
1731 (let ((p (get-buffer-process (current-buffer)))
1732 (found (progn (goto-char (point-min))
1733 (re-search-forward tramp-smb-prompt nil t)))
1734 (err (progn (goto-char (point-min))
1735 (re-search-forward tramp-smb-errors nil t)))
1736 buffer-read-only)
1737
1738 ;; Algorithm: get waiting output. See if last line contains
1739 ;; `tramp-smb-prompt' sentinel or `tramp-smb-errors' strings.
1740 ;; If not, wait a bit and again get waiting output.
1741 (while (and (not found) (not err) (memq (process-status p) '(run open)))
1742
1743 ;; Accept pending output.
1744 (tramp-accept-process-output p 0.1)
1745
1746 ;; Search for prompt.
1747 (goto-char (point-min))
1748 (setq found (re-search-forward tramp-smb-prompt nil t))
1749
1750 ;; Search for errors.
1751 (goto-char (point-min))
1752 (setq err (re-search-forward tramp-smb-errors nil t)))
1753
1754 ;; When the process is still alive, read pending output.
1755 (while (and (not found) (memq (process-status p) '(run open)))
1756
1757 ;; Accept pending output.
1758 (tramp-accept-process-output p 0.1)
1759
1760 ;; Search for prompt.
1761 (goto-char (point-min))
1762 (setq found (re-search-forward tramp-smb-prompt nil t)))
1763
1764 (tramp-message vec 6 "\n%s" (buffer-string))
1765
1766 ;; Remove prompt.
1767 (when found
1768 (goto-char (point-max))
1769 (re-search-backward tramp-smb-prompt nil t)
1770 (delete-region (point) (point-max)))
1771
1772 ;; Return value is whether no error message has appeared.
1773 (not err))))
1774
1775 (defun tramp-smb-kill-winexe-function ()
1776 "Send SIGKILL to the winexe process."
1777 (ignore-errors
1778 (let ((p (get-buffer-process (current-buffer))))
1779 (when (and p (processp p) (memq (process-status p) '(run open)))
1780 (signal-process (process-id p) 'SIGINT)))))
1781
1782 (defun tramp-smb-call-winexe (vec)
1783 "Apply a remote command, if possible, using `tramp-smb-winexe-program'."
1784
1785 ;; We call `tramp-get-buffer' in order to get a debug buffer for
1786 ;; messages.
1787 (tramp-get-buffer vec)
1788
1789 ;; Check for program.
1790 (unless (let ((default-directory
1791 (tramp-compat-temporary-file-directory)))
1792 (executable-find tramp-smb-winexe-program))
1793 (tramp-error
1794 vec 'file-error "Cannot find program: %s" tramp-smb-winexe-program))
1795
1796 ;; winexe does not supports ports.
1797 (when (tramp-file-name-port vec)
1798 (tramp-error vec 'file-error "Port not supported for remote processes"))
1799
1800 (tramp-smb-maybe-open-connection
1801 vec
1802 (format
1803 "%s %s"
1804 tramp-smb-winexe-shell-command tramp-smb-winexe-shell-command-switch))
1805
1806 (set (make-local-variable 'kill-buffer-hook)
1807 '(tramp-smb-kill-winexe-function))
1808
1809 ;; Suppress "^M". Shouldn't we specify utf8?
1810 (set-process-coding-system (tramp-get-connection-process vec) 'raw-text-dos)
1811
1812 ;; Set width to 128. This avoids mixing prompt and long error messages.
1813 (tramp-smb-send-command vec "$rawui = (Get-Host).UI.RawUI")
1814 (tramp-smb-send-command vec "$bufsize = $rawui.BufferSize")
1815 (tramp-smb-send-command vec "$winsize = $rawui.WindowSize")
1816 (tramp-smb-send-command vec "$bufsize.Width = 128")
1817 (tramp-smb-send-command vec "$winsize.Width = 128")
1818 (tramp-smb-send-command vec "$rawui.BufferSize = $bufsize")
1819 (tramp-smb-send-command vec "$rawui.WindowSize = $winsize"))
1820
1821 (defun tramp-smb-shell-quote-argument (s)
1822 "Similar to `shell-quote-argument', but uses windows cmd syntax."
1823 (let ((system-type 'ms-dos))
1824 (shell-quote-argument s)))
1825
1826 (add-hook 'tramp-unload-hook
1827 (lambda ()
1828 (unload-feature 'tramp-smb 'force)))
1829
1830 (provide 'tramp-smb)
1831
1832 ;;; TODO:
1833
1834 ;; * Return more comprehensive file permission string.
1835 ;; * Try to remove the inclusion of dummy "" directory. Seems to be at
1836 ;; several places, especially in `tramp-smb-handle-insert-directory'.
1837 ;; * Ignore case in file names.
1838
1839 ;;; tramp-smb.el ends here