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