* filenotify.el (file-notify--library): Renamed from
[bpt/emacs.git] / lisp / net / tramp-smb.el
CommitLineData
bce04fee 1;;; tramp-smb.el --- Tramp access functions for SMB servers
4007ba5b 2
ab422c4d 3;; Copyright (C) 2002-2013 Free Software Foundation, Inc.
4007ba5b 4
340b8d4f 5;; Author: Michael Albinus <michael.albinus@gmx.de>
4007ba5b 6;; Keywords: comm, processes
bd78fa1d 7;; Package: tramp
4007ba5b
KG
8
9;; This file is part of GNU Emacs.
10
874a927a 11;; GNU Emacs is free software: you can redistribute it and/or modify
4007ba5b 12;; it under the terms of the GNU General Public License as published by
874a927a
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
4007ba5b
KG
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
874a927a 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
4007ba5b
KG
23
24;;; Commentary:
25
26;; Access functions for SMB servers like SAMBA or M$ Windows from Tramp.
27
28;;; Code:
29
e8f28744 30(eval-when-compile (require 'cl)) ; block, return
4007ba5b
KG
31(require 'tramp)
32
4007ba5b 33;; Define SMB method ...
0f34aa77
MA
34;;;###tramp-autoload
35(defconst tramp-smb-method "smb"
fb7ada5f 36 "Method to connect SAMBA and M$ SMB servers.")
4007ba5b
KG
37
38;; ... and add it to the method list.
0f34aa77
MA
39;;;###tramp-autoload
40(unless (memq system-type '(cygwin windows-nt))
710dec63
MA
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 "")
2fe4b125 46 ;; This is just a guess. We don't know whether the share "C$"
710dec63
MA
47 ;; is available for public use, and whether the user has write
48 ;; access.
49 (tramp-tmpdir "/C$/Temp"))))
4007ba5b
KG
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.
b191c9d9 53;;;###tramp-autoload
4007ba5b 54(add-to-list 'tramp-default-method-alist
36a3859f 55 `(nil ,tramp-prefix-domain-regexp ,tramp-smb-method))
00d6fd04
MA
56
57;; Add a default for `tramp-default-user-alist'. Rule: For the SMB method,
58;; the anonymous user is chosen.
b191c9d9 59;;;###tramp-autoload
00d6fd04 60(add-to-list 'tramp-default-user-alist
66feec8b 61 `(,(concat "\\`" tramp-smb-method "\\'") nil nil))
4007ba5b
KG
62
63;; Add completion function for SMB method.
f8f91c2b
MA
64;;;###tramp-autoload
65(eval-after-load 'tramp
66 '(tramp-set-completion-function
67 tramp-smb-method
68 '((tramp-parse-netrc "~/.netrc"))))
4007ba5b
KG
69
70(defcustom tramp-smb-program "smbclient"
fb7ada5f 71 "Name of SMB client to run."
4007ba5b
KG
72 :group 'tramp
73 :type 'string)
74
0536254e 75(defcustom tramp-smb-conf "/dev/null"
fb7ada5f 76 "Path of the smb.conf file.
0536254e
MA
77If it is nil, no smb.conf will be added to the `tramp-smb-program'
78call, letting the SMB client use the default one."
79 :group 'tramp
80 :type '(choice (const nil) (file :must-match t)))
81
0c6f436e 82(defvar tramp-smb-version nil
fb7ada5f 83 "Version string of the SMB client.")
0c6f436e 84
2fe4b125
MA
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.")
4007ba5b
KG
97
98(defconst tramp-smb-errors
99 (mapconcat
100 'identity
39b20f56
MA
101 `(;; Connection error / timeout / unknown command.
102 "Connection\\( to \\S-+\\)? failed"
00d6fd04 103 "Read from server failed, maybe it closed the connection"
d037d501 104 "Call timed out: server did not respond"
c2dc9732
MA
105 "\\S-+: command not found"
106 "Server doesn't support UNIX CIFS calls"
39b20f56
MA
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),
710dec63
MA
119 ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003),
120 ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7).
39b20f56
MA
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"
710dec63 129 "NT_STATUS_IMAGE_ALREADY_LOADED"
39b20f56
MA
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")))
4007ba5b
KG
143 "\\|")
144 "Regexp for possible error strings of SMB servers.
145Used instead of analyzing error codes of commands.")
146
00d6fd04
MA
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.
154This list is used for login to SMB servers.
155
156See `tramp-actions-before-shell' for more info.")
4007ba5b 157
00d6fd04
MA
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.
164This list is used for login to SMB servers.
4007ba5b 165
00d6fd04 166See `tramp-actions-before-shell' for more info.")
8daea7fc 167
2fe4b125
MA
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.
174This list is used for tar-like copy of directories.
175
176See `tramp-actions-before-shell' for more info.")
177
4007ba5b
KG
178;; New handlers should be added here.
179(defconst tramp-smb-file-name-handler-alist
180 '(
c2dc9732 181 ;; `access-file' performed by default handler.
f6f7e059 182 (add-name-to-file . tramp-smb-handle-add-name-to-file)
c2dc9732
MA
183 ;; `byte-compiler-base-file-name' performed by default handler.
184 (copy-directory . tramp-smb-handle-copy-directory)
4007ba5b
KG
185 (copy-file . tramp-smb-handle-copy-file)
186 (delete-directory . tramp-smb-handle-delete-directory)
187 (delete-file . tramp-smb-handle-delete-file)
c2dc9732 188 ;; `diff-latest-backup-file' performed by default handler.
8daea7fc 189 (directory-file-name . tramp-handle-directory-file-name)
4007ba5b 190 (directory-files . tramp-smb-handle-directory-files)
c2dc9732 191 (directory-files-and-attributes
bd8fadca 192 . tramp-handle-directory-files-and-attributes)
00d6fd04
MA
193 (dired-call-process . ignore)
194 (dired-compress-file . ignore)
fffba733 195 (dired-uncache . tramp-handle-dired-uncache)
c2dc9732 196 (expand-file-name . tramp-smb-handle-expand-file-name)
4007ba5b 197 (file-accessible-directory-p . tramp-smb-handle-file-directory-p)
aca3d51d 198 (file-acl . tramp-smb-handle-file-acl)
4007ba5b
KG
199 (file-attributes . tramp-smb-handle-file-attributes)
200 (file-directory-p . tramp-smb-handle-file-directory-p)
bd8fadca
MA
201 (file-executable-p . tramp-handle-file-exists-p)
202 (file-exists-p . tramp-handle-file-exists-p)
4007ba5b
KG
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)
2c5b3bdd 206 (file-name-as-directory . tramp-handle-file-name-as-directory)
4007ba5b
KG
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)
c2dc9732 210 ;; `file-name-sans-versions' performed by default handler.
bd8fadca 211 (file-newer-than-file-p . tramp-handle-file-newer-than-file-p)
e06ec67f
MA
212 (file-notify-add-watch . ignore)
213 (file-notify-rm-watch . ignore)
214 (file-notify-supported-p . ignore)
00d6fd04 215 (file-ownership-preserved-p . ignore)
bd8fadca 216 (file-readable-p . tramp-handle-file-exists-p)
4007ba5b 217 (file-regular-p . tramp-handle-file-regular-p)
632c5478
MA
218 (file-remote-p . tramp-handle-file-remote-p)
219 ;; `file-selinux-context' performed by default handler.
00d6fd04 220 (file-symlink-p . tramp-handle-file-symlink-p)
c2dc9732 221 ;; `file-truename' performed by default handler.
4007ba5b 222 (file-writable-p . tramp-smb-handle-file-writable-p)
38c65fca 223 (find-backup-file-name . tramp-handle-find-backup-file-name)
c2dc9732
MA
224 ;; `find-file-noselect' performed by default handler.
225 ;; `get-file-buffer' performed by default handler.
4007ba5b
KG
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)
f6f7e059 231 (make-symbolic-link . tramp-smb-handle-make-symbolic-link)
2fe4b125 232 (process-file . tramp-smb-handle-process-file)
4007ba5b 233 (rename-file . tramp-smb-handle-rename-file)
53b6a8b1 234 (set-file-acl . ignore)
c2dc9732 235 (set-file-modes . tramp-smb-handle-set-file-modes)
4f752957 236 (set-file-selinux-context . ignore)
c2dc9732 237 (set-file-times . ignore)
00d6fd04 238 (set-visited-file-modtime . ignore)
2fe4b125
MA
239 (shell-command . tramp-handle-shell-command)
240 (start-file-process . tramp-smb-handle-start-file-process)
01917a18 241 (substitute-in-file-name . tramp-smb-handle-substitute-in-file-name)
4007ba5b 242 (unhandled-file-name-directory . tramp-handle-unhandled-file-name-directory)
00d6fd04
MA
243 (vc-registered . ignore)
244 (verify-visited-file-modtime . ignore)
4007ba5b
KG
245 (write-region . tramp-smb-handle-write-region)
246)
247 "Alist of handler functions for Tramp SMB method.
248Operations not mentioned here will be handled by the default Emacs primitives.")
249
2fe4b125
MA
250;; Options for remote processes via winexe.
251(defcustom tramp-smb-winexe-program "winexe"
252 "Name of winexe client to run.
253If it isn't found in the local $PATH, the absolute path of winexe
254shall be given. This is needed for remote processes."
255 :group 'tramp
256 :type 'string
2a1e2476 257 :version "24.3")
2fe4b125
MA
258
259(defcustom tramp-smb-winexe-shell-command "powershell.exe"
260 "Shell to be used for processes on remote machines.
261This must be Powershell V2 compatible."
262 :group 'tramp
263 :type 'string
2a1e2476 264 :version "24.3")
2fe4b125
MA
265
266(defcustom tramp-smb-winexe-shell-command-switch "-file -"
267 "Command switch used together with `tramp-smb-winexe-shell-command'.
268This can be used to disable echo etc."
269 :group 'tramp
270 :type 'string
2a1e2476 271 :version "24.3")
2fe4b125 272
b421decc
MA
273;; It must be a `defsubst' in order to push the whole code into
274;; tramp-loaddefs.el. Otherwise, there would be recursive autoloading.
0f34aa77
MA
275;;;###tramp-autoload
276(defsubst tramp-smb-file-name-p (filename)
4007ba5b 277 "Check if it's a filename for SMB servers."
2fe4b125
MA
278 (string= (tramp-file-name-method (tramp-dissect-file-name filename))
279 tramp-smb-method))
4007ba5b 280
0f34aa77 281;;;###tramp-autoload
4007ba5b
KG
282(defun tramp-smb-file-name-handler (operation &rest args)
283 "Invoke the SMB related OPERATION.
284First arg specifies the OPERATION, second arg is a list of arguments to
285pass to the OPERATION."
286 (let ((fn (assoc operation tramp-smb-file-name-handler-alist)))
287 (if fn
00d6fd04 288 (save-match-data (apply (cdr fn) args))
4007ba5b
KG
289 (tramp-run-real-handler operation args))))
290
0f34aa77
MA
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)))
4007ba5b
KG
295
296
c2dc9732
MA
297;; File name primitives.
298
f6f7e059
MA
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)
4260b402
MA
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))))))
f6f7e059 341
2fe4b125
MA
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
c2dc9732 357(defun tramp-smb-handle-copy-directory
a3fcfa99 358 (dirname newname &optional keep-date parents copy-contents)
2fe4b125 359 "Like `copy-directory' for Tramp files."
c2dc9732
MA
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
1d51f99c 365 (with-tramp-progress-reporter
2fe4b125 366 v 0 (format "Copying %s to %s" dirname newname)
288f783b 367 (cond
4260b402 368 ;; We must use a local temporary directory.
288f783b 369 ((and t1 t2)
288f783b
MA
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
ede9503b
MA
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))))
4260b402
MA
380
381 ;; We can copy recursively.
288f783b 382 ((or t1 t2)
2fe4b125
MA
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))
288f783b 391 (make-directory newname parents))
2fe4b125
MA
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
288f783b 435 (unwind-protect
2fe4b125
MA
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))))
4260b402
MA
487
488 ;; We must do it file-wise.
288f783b 489 (t
c2dc9732 490 (tramp-run-real-handler
2fe4b125 491 'copy-directory (list dirname newname keep-date parents))))))))
4007ba5b 492
4007ba5b 493(defun tramp-smb-handle-copy-file
632c5478 494 (filename newname &optional ok-if-already-exists keep-date
53b6a8b1 495 preserve-uid-gid preserve-extended-attributes)
00d6fd04 496 "Like `copy-file' for Tramp files.
2fe4b125 497KEEP-DATE has no effect in case NEWNAME resides on an SMB server.
53b6a8b1 498PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored."
4007ba5b
KG
499 (setq filename (expand-file-name filename)
500 newname (expand-file-name newname))
1d51f99c 501 (with-tramp-progress-reporter
3b30ccda
MA
502 (tramp-dissect-file-name (if (file-remote-p filename) filename newname))
503 0 (format "Copying %s to %s" filename newname)
504
2fe4b125
MA
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))))))
c2dc9732 538
2fe4b125
MA
539 ;; KEEP-DATE handling.
540 (when keep-date
541 (set-file-times newname (nth 5 (file-attributes filename))))))
4007ba5b 542
cc3dda16 543(defun tramp-smb-handle-delete-directory (directory &optional recursive)
00d6fd04 544 "Like `delete-directory' for Tramp files."
4007ba5b 545 (setq directory (directory-file-name (expand-file-name directory)))
340b8d4f 546 (when (file-exists-p directory)
cc3dda16
MA
547 (if recursive
548 (mapc
549 (lambda (file)
550 (if (file-directory-p file)
ede9503b 551 (tramp-compat-delete-directory file recursive)
4f201088 552 (delete-file file)))
cc3dda16
MA
553 ;; We do not want to delete "." and "..".
554 (directory-files
555 directory 'full "^\\([^.]\\|\\.\\([^.]\\|\\..\\)\\).*")))
4260b402 556
340b8d4f 557 (with-parsed-tramp-file-name directory nil
00d6fd04 558 ;; We must also flush the cache of the directory, because
f6f7e059 559 ;; `file-attributes' reads the values from there.
00d6fd04
MA
560 (tramp-flush-file-property v (file-name-directory localname))
561 (tramp-flush-directory-property v localname)
4260b402
MA
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))))))
4007ba5b 573
f1a5d776 574(defun tramp-smb-handle-delete-file (filename &optional trash)
00d6fd04 575 "Like `delete-file' for Tramp files."
4007ba5b 576 (setq filename (expand-file-name filename))
340b8d4f
MA
577 (when (file-exists-p filename)
578 (with-parsed-tramp-file-name filename nil
00d6fd04 579 ;; We must also flush the cache of the directory, because
f6f7e059 580 ;; `file-attributes' reads the values from there.
00d6fd04
MA
581 (tramp-flush-file-property v (file-name-directory localname))
582 (tramp-flush-file-property v localname)
4260b402
MA
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))))))
4007ba5b
KG
594
595(defun tramp-smb-handle-directory-files
596 (directory &optional full match nosort)
00d6fd04
MA
597 "Like `directory-files' for Tramp files."
598 (let ((result (mapcar 'directory-file-name
599 (file-name-all-completions "" directory))))
c2dc9732 600 ;; Discriminate with regexp.
00d6fd04
MA
601 (when match
602 (setq result
603 (delete nil
604 (mapcar (lambda (x) (when (string-match match x) x))
605 result))))
c2dc9732 606 ;; Append directory.
00d6fd04
MA
607 (when full
608 (setq result
609 (mapcar
610 (lambda (x) (expand-file-name x directory))
611 result)))
c2dc9732 612 ;; Sort them if necessary.
00d6fd04 613 (unless nosort (setq result (sort result 'string-lessp)))
c2dc9732 614 ;; That's it.
00d6fd04 615 result))
4007ba5b 616
c2dc9732
MA
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
c2dc9732 629 ;; Tilde expansion if necessary. We use the user name as share,
e1dbe924 630 ;; which is often the case in domains.
288f783b 631 (when (string-match "\\`/?~\\([^/]*\\)" localname)
c2dc9732
MA
632 (setq localname
633 (replace-match
288f783b 634 (if (zerop (length (match-string 1 localname)))
c2dc9732 635 (tramp-file-name-real-user v)
288f783b 636 (match-string 1 localname))
c2dc9732 637 nil nil localname)))
288f783b
MA
638 ;; Make the file name absolute.
639 (unless (tramp-run-real-handler 'file-name-absolute-p (list localname))
640 (setq localname (concat "/" localname)))
c2dc9732
MA
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
aca3d51d
MA
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))
a36e2d26 655 (while (looking-at "^#")
aca3d51d
MA
656 (forward-line)
657 (delete-region (point-min) (point)))
658 (goto-char (point-max))
659 (delete-blank-lines)
660 (when (> (point-max) (point-min))
a36e2d26
MA
661 (tramp-compat-funcall
662 'substring-no-properties (buffer-string))))))))
aca3d51d 663
c951aecb 664(defun tramp-smb-handle-file-attributes (filename &optional id-format)
00d6fd04 665 "Like `file-attributes' for Tramp files."
f6f7e059 666 (unless id-format (setq id-format 'integer))
710dec63
MA
667 (ignore-errors
668 (with-parsed-tramp-file-name filename nil
1d51f99c
MA
669 (with-tramp-file-property
670 v localname (format "file-attributes-%s" id-format)
710dec63
MA
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
f6f7e059 702
4260b402 703(defun tramp-smb-do-file-attributes-with-stat (vec &optional id-format)
f6f7e059 704 "Implement `file-attributes' for Tramp files using stat command."
4260b402
MA
705 (tramp-message
706 vec 5 "file attributes with stat: %s" (tramp-file-name-localname vec))
2fe4b125 707 (with-current-buffer (tramp-get-connection-buffer vec)
4260b402 708 (let* (size id link uid gid atime mtime ctime mode inode)
fc754ea1
MA
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)))))))
4007ba5b
KG
767
768(defun tramp-smb-handle-file-directory-p (filename)
00d6fd04
MA
769 "Like `file-directory-p' for Tramp files."
770 (and (file-exists-p filename)
771 (eq ?d (aref (nth 8 (file-attributes filename)) 0))))
4007ba5b 772
4007ba5b 773(defun tramp-smb-handle-file-local-copy (filename)
00d6fd04 774 "Like `file-local-copy' for Tramp files."
4007ba5b 775 (with-parsed-tramp-file-name filename nil
fffba733
MA
776 (unless (file-exists-p filename)
777 (tramp-error
778 v 'file-error
779 "Cannot make local copy of non-existing file `%s'" filename))
4260b402 780 (let ((tmpfile (tramp-compat-make-temp-file filename)))
1d51f99c 781 (with-tramp-progress-reporter
3b30ccda
MA
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.
eba082a2 787 (delete-file tmpfile)
3b30ccda
MA
788 (tramp-error
789 v 'file-error "Cannot make local copy of file `%s'" filename)))
94be87e8 790 tmpfile)))
4007ba5b
KG
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)
00d6fd04
MA
795 "Like `file-name-all-completions' for Tramp files."
796 (all-completions
797 filename
798 (with-parsed-tramp-file-name directory nil
1d51f99c 799 (with-tramp-file-property v localname "file-name-all-completions"
00d6fd04
MA
800 (save-match-data
801 (let ((entries (tramp-smb-get-file-entries directory)))
4007ba5b
KG
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
4007ba5b 810(defun tramp-smb-handle-file-writable-p (filename)
00d6fd04
MA
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)))))
4007ba5b
KG
817
818(defun tramp-smb-handle-insert-directory
819 (filename switches &optional wildcard full-directory-p)
00d6fd04 820 "Like `insert-directory' for Tramp files."
4007ba5b 821 (setq filename (expand-file-name filename))
d9320986
MA
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)))
340b8d4f 826 (with-parsed-tramp-file-name filename nil
4007ba5b 827 (save-match-data
4260b402 828 (let ((base (file-name-nondirectory filename))
00d6fd04
MA
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.
bf247b6e 843 (setq entries
00d6fd04
MA
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)))))
4007ba5b 856
adb67129 857 ;; Sort entries.
4007ba5b
KG
858 (setq entries
859 (sort
860 entries
861 (lambda (x y)
862 (if (string-match "t" switches)
00d6fd04
MA
863 ;; Sort by date.
864 (tramp-time-less-p (nth 3 y) (nth 3 x))
865 ;; Sort by name.
4007ba5b
KG
866 (string-lessp (nth 0 x) (nth 0 y))))))
867
adb67129
MA
868 ;; Handle "-F" switch.
869 (when (string-match "F" switches)
e61aad2f 870 (mapc
adb67129
MA
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
00d6fd04 880 ;; Print entries.
fc754ea1 881 (mapc
4007ba5b 882 (lambda (x)
00d6fd04 883 (when (not (zerop (length (nth 0 x))))
f6f7e059 884 (let ((attr
fc754ea1 885 (when (tramp-smb-get-stat-capability v)
4260b402 886 (ignore-errors
d9320986 887 (file-attributes filename 'string)))))
f6f7e059
MA
888 (insert
889 (format
fc754ea1 890 "%10s %3d %-8s %-8s %8s %s "
f6f7e059 891 (or (nth 8 attr) (nth 1 x)) ; mode
fc754ea1 892 (or (nth 1 attr) 1) ; inode
f6f7e059
MA
893 (or (nth 2 attr) "nobody") ; uid
894 (or (nth 3 attr) "nogroup") ; gid
fc754ea1 895 (or (nth 7 attr) (nth 2 x)) ; size
f6f7e059
MA
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")
fc754ea1 902 (nth 3 x)))) ; date
d5b3979c
MA
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'.
fc754ea1 906 (let ((start (point)))
d5b3979c
MA
907 (insert
908 (format
909 "%s\n"
d9320986
MA
910 (file-relative-name
911 (expand-file-name
912 (nth 0 x) (file-name-directory filename)))))
fc754ea1 913 (put-text-property start (1- (point)) 'dired-filename t))
f6f7e059
MA
914 (forward-line)
915 (beginning-of-line))))
916 entries)))))
4007ba5b
KG
917
918(defun tramp-smb-handle-make-directory (dir &optional parents)
00d6fd04 919 "Like `make-directory' for Tramp files."
4007ba5b
KG
920 (setq dir (directory-file-name (expand-file-name dir)))
921 (unless (file-name-absolute-p dir)
00d6fd04 922 (setq dir (expand-file-name dir default-directory)))
340b8d4f 923 (with-parsed-tramp-file-name dir nil
4007ba5b 924 (save-match-data
4260b402 925 (let* ((ldir (file-name-directory dir)))
c2dc9732 926 ;; Make missing directory parts.
4260b402
MA
927 (when (and parents
928 (tramp-smb-get-share v)
929 (not (file-directory-p ldir)))
4007ba5b 930 (make-directory ldir parents))
c2dc9732 931 ;; Just do it.
4007ba5b 932 (when (file-directory-p ldir)
8daea7fc 933 (make-directory-internal dir))
4007ba5b 934 (unless (file-directory-p dir)
00d6fd04 935 (tramp-error v 'file-error "Couldn't make directory %s" dir))))))
4007ba5b
KG
936
937(defun tramp-smb-handle-make-directory-internal (directory)
00d6fd04 938 "Like `make-directory-internal' for Tramp files."
4007ba5b
KG
939 (setq directory (directory-file-name (expand-file-name directory)))
940 (unless (file-name-absolute-p directory)
00d6fd04 941 (setq directory (expand-file-name directory default-directory)))
340b8d4f 942 (with-parsed-tramp-file-name directory nil
4007ba5b 943 (save-match-data
4260b402 944 (let* ((file (tramp-smb-get-localname v)))
4007ba5b 945 (when (file-directory-p (file-name-directory directory))
f6f7e059
MA
946 (tramp-smb-send-command
947 v
4260b402 948 (if (tramp-smb-get-cifs-capabilities v)
f6f7e059
MA
949 (format
950 "posix_mkdir \"%s\" %s"
0f34aa77 951 file (tramp-compat-decimal-to-octal (default-file-modes)))
f6f7e059 952 (format "mkdir \"%s\"" file)))
00d6fd04 953 ;; We must also flush the cache of the directory, because
f6f7e059
MA
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))
4007ba5b 957 (unless (file-directory-p directory)
00d6fd04
MA
958 (tramp-error
959 v 'file-error "Couldn't make directory %s" directory))))))
4007ba5b 960
f6f7e059
MA
961(defun tramp-smb-handle-make-symbolic-link
962 (filename linkname &optional ok-if-already-exists)
963 "Like `make-symbolic-link' for Tramp files.
964If LINKNAME is a non-Tramp file, it is used verbatim as the target of
965the symlink. If LINKNAME is a Tramp file, only the localname component is
966used as the target of the symlink.
967
968If LINKNAME is a Tramp file and the localname component is relative, then
969it is expanded first, before the localname component is taken. Note that
970this can give surprising results if the user/host for the source and
971target 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\""
4260b402
MA
1006 (tramp-smb-get-localname v1)
1007 (tramp-smb-get-localname v2)))
f6f7e059
MA
1008 (tramp-error
1009 v2 'file-error
1010 "error with make-symbolic-link, see buffer `%s' for details"
1011 (buffer-name))))))
1012
2fe4b125
MA
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
4007ba5b
KG
1134(defun tramp-smb-handle-rename-file
1135 (filename newname &optional ok-if-already-exists)
00d6fd04 1136 "Like `rename-file' for Tramp files."
4007ba5b
KG
1137 (setq filename (expand-file-name filename)
1138 newname (expand-file-name newname))
2fe4b125
MA
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
1d51f99c 1147 (with-tramp-progress-reporter
3b30ccda
MA
1148 (tramp-dissect-file-name (if (file-remote-p filename) filename newname))
1149 0 (format "Renaming %s to %s" filename newname)
1150
2fe4b125
MA
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))))
4007ba5b 1171
2fe4b125
MA
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)))))
4007ba5b 1177
c2dc9732
MA
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
f6f7e059
MA
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"
4260b402 1185 (tramp-smb-get-localname v)
0f34aa77 1186 (tramp-compat-decimal-to-octal mode)))
f6f7e059
MA
1187 (tramp-error
1188 v 'file-error "Error while changing file's mode %s" filename)))))
c2dc9732 1189
2fe4b125
MA
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
01917a18 1238(defun tramp-smb-handle-substitute-in-file-name (filename)
00d6fd04 1239 "Like `handle-substitute-in-file-name' for Tramp files.
b08104a0
MA
1240\"//\" substitutes only in the local filename part. Catches
1241errors 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)))))
01917a18
MA
1248 (condition-case nil
1249 (tramp-run-real-handler 'substitute-in-file-name (list filename))
1250 (error filename)))
1251
4007ba5b
KG
1252(defun tramp-smb-handle-write-region
1253 (start end filename &optional append visit lockname confirm)
00d6fd04 1254 "Like `write-region' for Tramp files."
4007ba5b 1255 (setq filename (expand-file-name filename))
340b8d4f 1256 (with-parsed-tramp-file-name filename nil
00d6fd04
MA
1257 (unless (eq append nil)
1258 (tramp-error
a4aeb9a4 1259 v 'file-error "Cannot append to file using Tramp (`%s')" filename))
94be87e8 1260 ;; XEmacs takes a coding system as the seventh argument, not `confirm'.
00d6fd04
MA
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
b08104a0 1267 ;; `file-attributes' reads the values from there.
00d6fd04
MA
1268 (tramp-flush-file-property v (file-name-directory localname))
1269 (tramp-flush-file-property v localname)
4260b402 1270 (let ((curbuf (current-buffer))
258800f8 1271 (tmpfile (tramp-compat-make-temp-file filename)))
00d6fd04
MA
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.
94be87e8
MA
1278 (list start end tmpfile append 'no-message lockname confirm)
1279 (list start end tmpfile append 'no-message lockname)))
00d6fd04 1280
1d51f99c 1281 (with-tramp-progress-reporter
3b30ccda
MA
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))
eba082a2 1288 (delete-file tmpfile)))
00d6fd04 1289
00d6fd04
MA
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)))))
4007ba5b
KG
1296
1297
c2dc9732 1298;; Internal file name functions.
4007ba5b 1299
4260b402 1300(defun tramp-smb-get-share (vec)
7432277c 1301 "Returns the share name of LOCALNAME."
4007ba5b 1302 (save-match-data
4260b402
MA
1303 (let ((localname (tramp-file-name-localname vec)))
1304 (when (string-match "^/?\\([^/]+\\)/" localname)
1305 (match-string 1 localname)))))
4007ba5b 1306
4260b402 1307(defun tramp-smb-get-localname (vec)
7432277c 1308 "Returns the file name of LOCALNAME.
4260b402 1309If VEC has no cifs capabilities, exchange \"/\" by \"\\\\\"."
4007ba5b 1310 (save-match-data
4260b402 1311 (let ((localname (tramp-file-name-localname vec)))
4007ba5b 1312 (setq
4260b402
MA
1313 localname
1314 (if (string-match "^/?[^/]+\\(/.*\\)" localname)
22bcf204 1315 ;; There is a share, separated by "/".
4260b402
MA
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 "")))
4007ba5b 1325
c2dc9732 1326 ;; Sometimes we have discarded `substitute-in-file-name'.
4260b402
MA
1327 (when (string-match "\\(\\$\\$\\)\\(/\\|$\\)" localname)
1328 (setq localname (replace-match "$" nil nil localname 1)))
4007ba5b 1329
4260b402 1330 localname)))
4007ba5b
KG
1331
1332;; Share names of a host are cached. It is very unlikely that the
1333;; shares do change during connection.
00d6fd04
MA
1334(defun tramp-smb-get-file-entries (directory)
1335 "Read entries which match DIRECTORY.
4007ba5b 1336Either the shares are listed, or the `dir' command is executed.
7432277c 1337Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME YEAR)."
4260b402 1338 (with-parsed-tramp-file-name (file-name-as-directory directory) nil
00d6fd04 1339 (setq localname (or localname "/"))
1d51f99c 1340 (with-tramp-file-property v localname "file-entries"
2fe4b125 1341 (with-current-buffer (tramp-get-connection-buffer v)
4260b402 1342 (let* ((share (tramp-smb-get-share v))
00d6fd04
MA
1343 (cache (tramp-get-connection-property v "share-cache" nil))
1344 res entry)
1345
1346 (if (and (not share) cache)
c2dc9732 1347 ;; Return cached shares.
00d6fd04
MA
1348 (setq res cache)
1349
c2dc9732 1350 ;; Read entries.
00d6fd04 1351 (if share
4260b402
MA
1352 (tramp-smb-send-command
1353 v (format "dir \"%s*\"" (tramp-smb-get-localname v)))
c2dc9732 1354 ;; `tramp-smb-maybe-open-connection' lists also the share names.
00d6fd04
MA
1355 (tramp-smb-maybe-open-connection v))
1356
c2dc9732 1357 ;; Loop the listing.
00d6fd04 1358 (goto-char (point-min))
4260b402
MA
1359 (if (re-search-forward tramp-smb-errors nil t)
1360 (tramp-error v 'file-error "%s `%s'" (match-string 0) directory)
00d6fd04
MA
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
c2dc9732 1366 ;; Cache share entries.
00d6fd04
MA
1367 (unless share
1368 (tramp-set-connection-property v "share-cache" res)))
4007ba5b 1369
c2dc9732 1370 ;; Add directory itself.
00d6fd04 1371 (add-to-list 'res '("" "drwxrwxrwx" 0 (0 0)))
5ec2cc41 1372
00d6fd04
MA
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))
4007ba5b 1376
c2dc9732 1377 ;; Return entries.
00d6fd04 1378 (delq nil res))))))
4007ba5b 1379
c2dc9732 1380;; Return either a share name (if SHARE is nil), or a file name.
4007ba5b 1381;;
c2dc9732 1382;; If shares are listed, the following format is expected:
4007ba5b 1383;;
4260b402
MA
1384;; Disk| - leading spaces
1385;; [^|]+| - share name, 14 char
1386;; .* - comment
4007ba5b
KG
1387;;
1388;; Entries provided by smbclient DIR aren't fully regular.
1389;; They should have the format
1390;;
1391;; \s-\{2,2} - leading spaces
b1a2b924
KG
1392;; \S-\(.*\S-\)\s-* - file name, 30 chars, left bound
1393;; \s-+[ADHRSV]* - permissions, 7 chars, right bound
39b20f56 1394;; \s- - space delimiter
b1a2b924 1395;; \s-+[0-9]+ - size, 8 chars, right bound
39b20f56 1396;; \s-\{2,2\} - space delimiter
4007ba5b 1397;; \w\{3,3\} - weekday
39b20f56 1398;; \s- - space delimiter
b1a2b924 1399;; \w\{3,3\} - month
39b20f56 1400;; \s- - space delimiter
00d6fd04 1401;; [ 12][0-9] - day
39b20f56 1402;; \s- - space delimiter
4007ba5b 1403;; [0-9]\{2,2\}:[0-9]\{2,2\}:[0-9]\{2,2\} - time
39b20f56 1404;; \s- - space delimiter
4007ba5b
KG
1405;; [0-9]\{4,4\} - year
1406;;
b1a2b924
KG
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;;
4007ba5b
KG
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.
1434If SHARE is result, entries are of type dir. Otherwise, shares are listed.
7432277c 1435Result is the list (LOCALNAME MODE SIZE MTIME)."
00d6fd04
MA
1436;; We are called from `tramp-smb-get-file-entries', which sets the
1437;; current buffer.
6e060cee 1438 (let ((line (buffer-substring (point) (point-at-eol)))
7432277c 1439 localname mode size month day hour min sec year mtime)
4007ba5b
KG
1440
1441 (if (not share)
1442
00d6fd04 1443 ;; Read share entries.
4260b402 1444 (when (string-match "^Disk|\\([^|]+\\)|" line)
7432277c 1445 (setq localname (match-string 1 line)
4007ba5b
KG
1446 mode "dr-xr-xr-x"
1447 size 0))
1448
00d6fd04 1449 ;; Real listing.
4007ba5b
KG
1450 (block nil
1451
c2dc9732 1452 ;; year.
4007ba5b
KG
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
c2dc9732 1458 ;; time.
4007ba5b
KG
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
c2dc9732 1466 ;; day.
4007ba5b
KG
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
c2dc9732 1472 ;; month.
4007ba5b
KG
1473 (if (string-match "\\(\\w+\\)$" line)
1474 (setq month (match-string 1 line)
1475 line (substring line 0 -4))
1476 (return))
1477
c2dc9732 1478 ;; weekday.
4007ba5b
KG
1479 (if (string-match "\\(\\w+\\)$" line)
1480 (setq line (substring line 0 -5))
1481 (return))
1482
c2dc9732 1483 ;; size.
4007ba5b 1484 (if (string-match "\\([0-9]+\\)$" line)
b1a2b924
KG
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)))
4007ba5b
KG
1490 (return))
1491
c2dc9732 1492 ;; mode: ARCH, DIR, HIDDEN, RONLY, SYSTEM, VOLID.
b1a2b924 1493 (if (string-match "\\([ADHRSV]+\\)?$" line)
4007ba5b 1494 (setq
b1a2b924 1495 mode (or (match-string 1 line) "")
4007ba5b
KG
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"))))
4260b402 1502 line (substring line 0 -6))
4007ba5b
KG
1503 (return))
1504
c2dc9732 1505 ;; localname.
b1a2b924 1506 (if (string-match "^\\s-+\\(\\S-\\(.*\\S-\\)?\\)\\s-*$" line)
7432277c 1507 (setq localname (match-string 1 line))
4007ba5b
KG
1508 (return))))
1509
7432277c 1510 (when (and localname mode size)
4007ba5b
KG
1511 (setq mtime
1512 (if (and sec min hour day month year)
1513 (encode-time
1514 sec min hour day
00d6fd04 1515 (cdr (assoc (downcase month) tramp-parse-time-months))
4007ba5b
KG
1516 year)
1517 '(0 0)))
7432277c 1518 (list localname mode size mtime))))
4007ba5b 1519
f6f7e059
MA
1520(defun tramp-smb-get-cifs-capabilities (vec)
1521 "Check, whether the SMB server supports POSIX commands."
4260b402
MA
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))))
1d51f99c 1525 (with-tramp-connection-property
4260b402 1526 (tramp-get-connection-process vec) "cifs-capabilities"
fc754ea1
MA
1527 (save-match-data
1528 (when (tramp-smb-send-command vec "posix")
2fe4b125 1529 (with-current-buffer (tramp-get-connection-buffer vec)
fc754ea1
MA
1530 (goto-char (point-min))
1531 (when
1532 (re-search-forward "Server supports CIFS capabilities" nil t)
1533 (member
1534 "pathnames"
1535 (split-string
6e060cee 1536 (buffer-substring (point) (point-at-eol)) nil t)))))))))
fc754ea1
MA
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))))
1d51f99c 1543 (with-tramp-connection-property
fc754ea1
MA
1544 (tramp-get-connection-process vec) "stat-capability"
1545 (tramp-smb-send-command vec "stat ."))))
f6f7e059 1546
4007ba5b 1547
c2dc9732 1548;; Connection functions.
4007ba5b 1549
00d6fd04
MA
1550(defun tramp-smb-send-command (vec command)
1551 "Send the COMMAND to connection VEC.
1552Returns 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
2fe4b125 1558(defun tramp-smb-maybe-open-connection (vec &optional argument)
00d6fd04 1559 "Maybe open a connection to HOST, log in as USER, using `tramp-smb-program'.
4007ba5b 1560Does not do anything if a connection is already open, but re-opens the
2fe4b125
MA
1561connection if a previous connection has died for some reason.
1562If ARGUMENT is non-nil, use it as argument for
1563`tramp-smb-winexe-program', and suppress any checks."
4260b402 1564 (let* ((share (tramp-smb-get-share vec))
2fe4b125 1565 (buf (tramp-get-connection-buffer vec))
00d6fd04 1566 (p (get-buffer-process buf)))
340b8d4f 1567
c2dc9732
MA
1568 ;; Check whether we still have the same smbclient version.
1569 ;; Otherwise, we must delete the connection cache, because
1570 ;; capabilities migh have changed.
2fe4b125 1571 (unless (or argument (processp p))
fc754ea1
MA
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))
4260b402
MA
1591 (tramp-flush-directory-property vec "")
1592 (tramp-flush-connection-property vec))
fc754ea1
MA
1593
1594 (tramp-set-connection-property
1595 vec "smbclient-version" tramp-smb-version)))
c2dc9732 1596
00d6fd04 1597 ;; If too much time has passed since last command was sent, look
c2dc9732
MA
1598 ;; whether there has been an error message; maybe due to
1599 ;; connection timeout.
00d6fd04
MA
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))
2fe4b125
MA
1615 (or argument
1616 (string-equal
1617 share
1618 (tramp-get-connection-property p "smb-share" ""))))
00d6fd04
MA
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
36a3859f
MA
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)
00d6fd04 1632
2fe4b125
MA
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 ))))
00d6fd04
MA
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))))
0536254e
MA
1647 (when tramp-smb-conf
1648 (setq args (append args (list "-s" tramp-smb-conf))))
2fe4b125
MA
1649 (when argument
1650 (setq args (append args (list argument))))
00d6fd04
MA
1651
1652 ;; OK, let's go.
1d51f99c 1653 (with-tramp-progress-reporter
3b30ccda
MA
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
2fe4b125
MA
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))))
3b30ccda
MA
1669
1670 (tramp-message
1671 vec 6 "%s" (mapconcat 'identity (process-command p) " "))
bd8fadca 1672 (tramp-compat-set-process-query-on-exit-flag p nil)
3b30ccda
MA
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
2fe4b125
MA
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
f7eac6d8
MA
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.
2fe4b125 1707 (tramp-set-connection-property p "smb-share" share)
f7eac6d8 1708 (tramp-set-connection-property p "chunksize" 1))
2fe4b125
MA
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)))))))))))))
4007ba5b
KG
1725
1726;; We don't use timeouts. If needed, the caller shall wrap around.
00d6fd04 1727(defun tramp-smb-wait-for-output (vec)
4007ba5b 1728 "Wait for output from smbclient command.
4007ba5b 1729Returns nil if an error message has appeared."
2fe4b125 1730 (with-current-buffer (tramp-get-connection-buffer vec)
00d6fd04
MA
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))
710dec63
MA
1735 (re-search-forward tramp-smb-errors nil t)))
1736 buffer-read-only)
5ec2cc41 1737
00d6fd04 1738 ;; Algorithm: get waiting output. See if last line contains
710dec63 1739 ;; `tramp-smb-prompt' sentinel or `tramp-smb-errors' strings.
00d6fd04 1740 ;; If not, wait a bit and again get waiting output.
710dec63 1741 (while (and (not found) (not err) (memq (process-status p) '(run open)))
5ec2cc41 1742
00d6fd04 1743 ;; Accept pending output.
f7eac6d8 1744 (tramp-accept-process-output p 0.1)
4007ba5b 1745
00d6fd04
MA
1746 ;; Search for prompt.
1747 (goto-char (point-min))
1748 (setq found (re-search-forward tramp-smb-prompt nil t))
4007ba5b 1749
00d6fd04
MA
1750 ;; Search for errors.
1751 (goto-char (point-min))
1752 (setq err (re-search-forward tramp-smb-errors nil t)))
4007ba5b 1753
00d6fd04
MA
1754 ;; When the process is still alive, read pending output.
1755 (while (and (not found) (memq (process-status p) '(run open)))
4007ba5b 1756
00d6fd04 1757 ;; Accept pending output.
f7eac6d8 1758 (tramp-accept-process-output p 0.1)
4007ba5b 1759
00d6fd04
MA
1760 ;; Search for prompt.
1761 (goto-char (point-min))
1762 (setq found (re-search-forward tramp-smb-prompt nil t)))
4007ba5b 1763
00d6fd04 1764 (tramp-message vec 6 "\n%s" (buffer-string))
2fe4b125
MA
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.
00d6fd04 1773 (not err))))
4007ba5b 1774
2fe4b125
MA
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
0f34aa77
MA
1826(add-hook 'tramp-unload-hook
1827 (lambda ()
1828 (unload-feature 'tramp-smb 'force)))
4007ba5b 1829
4007ba5b
KG
1830(provide 'tramp-smb)
1831
1832;;; TODO:
1833
c2dc9732 1834;; * Return more comprehensive file permission string.
4007ba5b
KG
1835;; * Try to remove the inclusion of dummy "" directory. Seems to be at
1836;; several places, especially in `tramp-smb-handle-insert-directory'.
710dec63 1837;; * Ignore case in file names.
4007ba5b
KG
1838
1839;;; tramp-smb.el ends here