+
-
-
+
+
@@ -211,11 +211,13 @@
+
+
@@ -228,6 +230,7 @@
@@ -417,6 +431,6 @@
-
+
diff --git a/code/apps/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings
index 998e5003..47fdb80d 100644
--- a/code/apps/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Centre";
"CFBundleDisplayName" = "Managed Software Centre";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings
index 998e5003..47fdb80d 100644
--- a/code/apps/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Centre";
"CFBundleDisplayName" = "Managed Software Centre";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings
index 998e5003..47fdb80d 100644
--- a/code/apps/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Centre";
"CFBundleDisplayName" = "Managed Software Centre";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings
index e539cb05..ac9d4d39 100644
--- a/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Centro de aplicaciones";
"CFBundleDisplayName" = "Centro de aplicaciones";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
diff --git a/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings
index ec46d6c7..d5dca659 100644
--- a/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings
index 561405de..d3856dba 100644
--- a/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Centre de gestion des logiciels";
"CFBundleDisplayName" = "Centre de gestion des logiciels";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings
index 507d13d6..8064dbbf 100644
--- a/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Centro Gestione Applicazioni";
"CFBundleDisplayName" = "Centro Gestione Applicazioni";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/main.m b/code/apps/Managed Software Center/Managed Software Center/main.m
index 3a6216cf..44d06625 100644
--- a/code/apps/Managed Software Center/Managed Software Center/main.m
+++ b/code/apps/Managed Software Center/Managed Software Center/main.m
@@ -2,7 +2,7 @@
// main.m
// Managed Software Center
//
-// Copyright 2013-2014 Greg Neagle.
+// Copyright 2013-2016 Greg Neagle.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/code/apps/Managed Software Center/Managed Software Center/msclib.py b/code/apps/Managed Software Center/Managed Software Center/msclib.py
index e3360ca6..3e8601e0 100644
--- a/code/apps/Managed Software Center/Managed Software Center/msclib.py
+++ b/code/apps/Managed Software Center/Managed Software Center/msclib.py
@@ -3,7 +3,7 @@
# msclib.py
#
# Created by Greg Neagle on 12/10/13.
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/Managed Software Center/Managed Software Center/msclog.py b/code/apps/Managed Software Center/Managed Software Center/msclog.py
index c838289b..d5c90526 100644
--- a/code/apps/Managed Software Center/Managed Software Center/msclog.py
+++ b/code/apps/Managed Software Center/Managed Software Center/msclog.py
@@ -6,7 +6,7 @@
# Created by Greg Neagle on 2/23/14.
# Original by John Randolph
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/Managed Software Center/Managed Software Center/munki.py b/code/apps/Managed Software Center/Managed Software Center/munki.py
index 71f58cd4..06841ea0 100644
--- a/code/apps/Managed Software Center/Managed Software Center/munki.py
+++ b/code/apps/Managed Software Center/Managed Software Center/munki.py
@@ -4,7 +4,7 @@
# Managed Software Center
#
# Created by Greg Neagle on 2/11/10.
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings
index ec46d6c7..d5dca659 100644
--- a/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings
index ec46d6c7..d5dca659 100644
--- a/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings
index 2bb3bd14..03cf82be 100644
--- a/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Центр Управления ПО";
"CFBundleDisplayName" = "Центр Управления ПО";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
diff --git a/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings
index ec46d6c7..d5dca659 100644
--- a/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings
+++ b/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/Managed Software Center/Managed Software Center/templates/myitems_row_template.html b/code/apps/Managed Software Center/Managed Software Center/templates/myitems_row_template.html
index 7e778308..4d9c42c0 100644
--- a/code/apps/Managed Software Center/Managed Software Center/templates/myitems_row_template.html
+++ b/code/apps/Managed Software Center/Managed Software Center/templates/myitems_row_template.html
@@ -7,12 +7,12 @@
- - ${developer_escaped}
+ - ${developer_escaped}
- ${version_to_install} |
- ${size} |
-
+ | ${version_to_install} |
+ ${size} |
+
${status_text}
diff --git a/code/apps/Managed Software Center/Managed Software Center/updatesTemplate.png b/code/apps/Managed Software Center/Managed Software Center/updatesTemplate.png
index 0153c522..dd645692 100644
Binary files a/code/apps/Managed Software Center/Managed Software Center/updatesTemplate.png and b/code/apps/Managed Software Center/Managed Software Center/updatesTemplate.png differ
diff --git a/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.h b/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.h
index cd9de808..de110dd5 100644
--- a/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.h
+++ b/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.h
@@ -2,7 +2,7 @@
// BorderlessWindow.h
//
// Created by Greg Neagle on 5/16/09.
-// Copyright 2009-2014 Greg Neagle.
+// Copyright 2009-2016 Greg Neagle.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.m b/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.m
index 616093ea..33a314f2 100644
--- a/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.m
+++ b/code/apps/MunkiStatus/MunkiStatus/BorderlessWindow.m
@@ -2,7 +2,7 @@
// BorderlessWindow.m
//
// Created by Greg Neagle on 5/16/09.
-// Copyright 2009-2014 Greg Neagle.
+// Copyright 2009-2016 Greg Neagle.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/FoundationPlist.py b/code/apps/MunkiStatus/MunkiStatus/FoundationPlist.py
index 8832b8f7..b89ddee8 100644
--- a/code/apps/MunkiStatus/MunkiStatus/FoundationPlist.py
+++ b/code/apps/MunkiStatus/MunkiStatus/FoundationPlist.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/MSUAppDelegate.py b/code/apps/MunkiStatus/MunkiStatus/MSUAppDelegate.py
index 2b5d4f91..37de9818 100644
--- a/code/apps/MunkiStatus/MunkiStatus/MSUAppDelegate.py
+++ b/code/apps/MunkiStatus/MunkiStatus/MSUAppDelegate.py
@@ -3,7 +3,7 @@
# MSUAppDelegate.py
# MunkiStatus
#
-# Copyright 2013-2014 Greg Neagle.
+# Copyright 2013-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py b/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py
index df2f3f57..0402fcb2 100644
--- a/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py
+++ b/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py
@@ -3,7 +3,7 @@
# MSUStatusWindowController.py
# MunkiStatus
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist b/code/apps/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist
index a65c3fc6..933fc52c 100644
--- a/code/apps/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist
+++ b/code/apps/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist
@@ -29,7 +29,7 @@
LSMinimumSystemVersion
${MACOSX_DEPLOYMENT_TARGET}
NSHumanReadableCopyright
- Copyright © 2014 The Munki Project https://github.com/munki/munki
+ Copyright © 2016 The Munki Project https://github.com/munki/munki
NSMainNibFile
MainMenu
NSPrincipalClass
diff --git a/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.h b/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.h
index 350f1a72..a22d97bd 100644
--- a/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.h
+++ b/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.h
@@ -3,7 +3,7 @@
//
// Created by Greg Neagle on 5/27/09.
//
-// Copyright 2009-2014 Greg Neagle.
+// Copyright 2009-2016 Greg Neagle.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.m b/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.m
index 774ea545..ba3a0397 100644
--- a/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.m
+++ b/code/apps/MunkiStatus/MunkiStatus/ScaledImageView.m
@@ -3,7 +3,7 @@
//
// Created by Greg Neagle on 5/27/09.
//
-// Copyright 2009-2014 Greg Neagle.
+// Copyright 2009-2016 Greg Neagle.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings b/code/apps/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings
index 03d72fec..993f24d3 100644
--- a/code/apps/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings
+++ b/code/apps/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "MunkiStatus";
"CFBundleDisplayName" = "MunkiStatus";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings b/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings
index 561405de..d3856dba 100644
--- a/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings
+++ b/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "Centre de gestion des logiciels";
"CFBundleDisplayName" = "Centre de gestion des logiciels";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings b/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings
index 03d72fec..993f24d3 100644
--- a/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings
+++ b/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings
@@ -2,4 +2,4 @@
"CFBundleName" = "MunkiStatus";
"CFBundleDisplayName" = "MunkiStatus";
-NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
+NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
\ No newline at end of file
diff --git a/code/apps/MunkiStatus/MunkiStatus/main.m b/code/apps/MunkiStatus/MunkiStatus/main.m
index 7873adc6..199655e3 100644
--- a/code/apps/MunkiStatus/MunkiStatus/main.m
+++ b/code/apps/MunkiStatus/MunkiStatus/main.m
@@ -2,7 +2,7 @@
// main.m
// MunkiStatus
//
-// Copyright 2013-2014 Greg Neagle.
+// Copyright 2013-2016 Greg Neagle.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/main.py b/code/apps/MunkiStatus/MunkiStatus/main.py
index c8ce6a4a..b06ad9cc 100644
--- a/code/apps/MunkiStatus/MunkiStatus/main.py
+++ b/code/apps/MunkiStatus/MunkiStatus/main.py
@@ -2,7 +2,7 @@
# main.py
# MunkiStatus
#
-# Copyright 2013-2014 Greg Neagle.
+# Copyright 2013-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/apps/MunkiStatus/MunkiStatus/munki.py b/code/apps/MunkiStatus/MunkiStatus/munki.py
index e8d8fa69..3cc0deac 100644
--- a/code/apps/MunkiStatus/MunkiStatus/munki.py
+++ b/code/apps/MunkiStatus/MunkiStatus/munki.py
@@ -4,7 +4,7 @@
# MunkiStatus
#
# Created by Greg Neagle on 2/11/10.
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/iconimporter b/code/client/iconimporter
index d1ef21c4..be350a0a 100755
--- a/code/client/iconimporter
+++ b/code/client/iconimporter
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/client/launchapp b/code/client/launchapp
index c4496b36..fc7aec8f 100755
--- a/code/client/launchapp
+++ b/code/client/launchapp
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/logouthelper b/code/client/logouthelper
index 6ee25c76..a428855a 100755
--- a/code/client/logouthelper
+++ b/code/client/logouthelper
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2011-2014 Greg Neagle.
+# Copyright 2011-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/makecatalogs b/code/client/makecatalogs
index c8fa7fc8..d42cdb34 100755
--- a/code/client/makecatalogs
+++ b/code/client/makecatalogs
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/makepkginfo b/code/client/makepkginfo
index 19272bb3..8c4b3186 100755
--- a/code/client/makepkginfo
+++ b/code/client/makepkginfo
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2008-2014 Greg Neagle.
+# Copyright 2008-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/managedsoftwareupdate b/code/client/managedsoftwareupdate
index 8a2926ae..2bce6245 100755
--- a/code/client/managedsoftwareupdate
+++ b/code/client/managedsoftwareupdate
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
@@ -634,7 +634,7 @@ def main():
munkicommon.log("### Starting managedsoftwareupdate run: %s ###" % runtype)
if options.verbose:
print 'Managed Software Update Tool'
- print 'Copyright 2010-2014 The Munki Project'
+ print 'Copyright 2010-2016 The Munki Project'
print 'https://github.com/munki/munki\n'
munkicommon.display_status_major('Starting...')
@@ -710,6 +710,7 @@ def main():
if options.manualcheck:
# record our result
recordUpdateCheckResult(-1)
+ if options.munkistatusoutput:
# connect to socket and quit
munkistatus.activate()
munkistatus.quit()
diff --git a/code/client/manifestutil b/code/client/manifestutil
index 4c0a741b..0a6c4073 100755
--- a/code/client/manifestutil
+++ b/code/client/manifestutil
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2011-2014 Greg Neagle.
+# Copyright 2011-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkiimport b/code/client/munkiimport
index f88514dd..e3501287 100755
--- a/code/client/munkiimport
+++ b/code/client/munkiimport
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/FoundationPlist.py b/code/client/munkilib/FoundationPlist.py
index 1bfc2dac..f96705f9 100644
--- a/code/client/munkilib/FoundationPlist.py
+++ b/code/client/munkilib/FoundationPlist.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/adobeutils.py b/code/client/munkilib/adobeutils.py
index 98fe1077..95ae9672 100644
--- a/code/client/munkilib/adobeutils.py
+++ b/code/client/munkilib/adobeutils.py
@@ -7,7 +7,7 @@ Utilities to enable munki to install/uninstall Adobe CS3/CS4/CS5 products
using the CS3/CS4/CS5 Deployment Toolkits.
"""
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/appleupdates.py b/code/client/munkilib/appleupdates.py
index ae395fb8..ebb35f53 100755
--- a/code/client/munkilib/appleupdates.py
+++ b/code/client/munkilib/appleupdates.py
@@ -6,7 +6,7 @@ appleupdates.py
Utilities for dealing with Apple Software Update.
"""
-# Copyright 2009-2015 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -1610,7 +1610,7 @@ class AppleUpdates(object):
if key in metadata_to_copy and metadata[key]:
if key == 'RestartAction':
# Ensure that a heavier weighted 'RestartAction' is not
- # overriden by one supplied in metadata
+ # overridden by one supplied in metadata
if metadata[key] not in RestartActions.get(
item.get(key, 'None')):
munkicommon.display_debug2(
diff --git a/code/client/munkilib/fetch.py b/code/client/munkilib/fetch.py
index cfcd7de0..f2420a30 100644
--- a/code/client/munkilib/fetch.py
+++ b/code/client/munkilib/fetch.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2011-2014 Greg Neagle.
+# Copyright 2011-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/gurl.py b/code/client/munkilib/gurl.py
index a1918ad2..71bfb79f 100644
--- a/code/client/munkilib/gurl.py
+++ b/code/client/munkilib/gurl.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
gurl.py
Created by Greg Neagle on 2013-11-21.
+Modified in Feb 2016 to add support for NSURLSession.
curl replacement using NSURLConnection and friends
"""
@@ -32,22 +33,63 @@ from objc import super
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
# No name 'Foo' in module 'Bar' warnings. Disable them.
# pylint: disable=E0611
-from Foundation import NSBundle
-from Foundation import NSRunLoop, NSDate
-from Foundation import NSObject, NSURL, NSURLConnection
-from Foundation import NSMutableURLRequest
-from Foundation import NSURLRequestReloadIgnoringLocalCacheData
-from Foundation import NSURLResponseUnknownLength
-from Foundation import NSLog
-from Foundation import NSURLCredential, NSURLCredentialPersistenceNone
-from Foundation import NSPropertyListSerialization
-from Foundation import NSPropertyListMutableContainersAndLeaves
-from Foundation import NSPropertyListXMLFormat_v1_0
-# pylint: enable=E0611
+from Foundation import NSBundle, \
+ NSRunLoop, NSDate, \
+ NSObject, NSURL, NSURLConnection, \
+ NSMutableURLRequest, \
+ NSURLRequestReloadIgnoringLocalCacheData, \
+ NSURLResponseUnknownLength, \
+ NSLog, \
+ NSURLCredential, NSURLCredentialPersistenceNone, \
+ NSPropertyListSerialization, \
+ NSPropertyListMutableContainersAndLeaves, \
+ NSPropertyListXMLFormat_v1_0
+
+try:
+ from Foundation import NSURLSession, NSURLSessionConfiguration
+ NSURLSESSION_AVAILABLE = True
+except ImportError:
+ NSURLSESSION_AVAILABLE = False
# Disable PyLint complaining about 'invalid' names
# pylint: disable=C0103
+if NSURLSESSION_AVAILABLE:
+ # NSURLSessionAuthChallengeDisposition enum constants
+ NSURLSessionAuthChallengeUseCredential = 0
+ NSURLSessionAuthChallengePerformDefaultHandling = 1
+ NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2
+ NSURLSessionAuthChallengeRejectProtectionSpace = 3
+
+ # NSURLSessionResponseDisposition enum constants
+ NSURLSessionResponseCancel = 0
+ NSURLSessionResponseAllow = 1
+ NSURLSessionResponseBecomeDownload = 2
+
+ # TLS/SSLProtocol enum constants
+ kSSLProtocolUnknown = 0
+ kSSLProtocol3 = 2
+ kTLSProtocol1 = 4
+ kTLSProtocol11 = 7
+ kTLSProtocol12 = 8
+ kDTLSProtocol1 = 9
+
+ # define a helper function for block callbacks
+ import ctypes
+ import objc
+ _objc_so = ctypes.cdll.LoadLibrary(
+ os.path.join(objc.__path__[0], '_objc.so'))
+ PyObjCMethodSignature_WithMetaData = (
+ _objc_so.PyObjCMethodSignature_WithMetaData)
+ PyObjCMethodSignature_WithMetaData.restype = ctypes.py_object
+
+ def objc_method_signature(signature_str):
+ '''Return a PyObjCMethodSignature given a call signature in string
+ format'''
+ return PyObjCMethodSignature_WithMetaData(
+ ctypes.create_string_buffer(signature_str), None, False)
+
+# pylint: enable=E0611
# disturbing hack warning!
# this works around an issue with App Transport Security on 10.11
@@ -56,6 +98,11 @@ info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
info['NSAppTransportSecurity'] = {'NSAllowsArbitraryLoads': True}
+def NSLogWrapper(message):
+ '''A wrapper function for NSLog to prevent format string errors'''
+ NSLog('%@', message)
+
+
ssl_error_codes = {
-9800: u'SSL protocol error',
-9801: u'Cipher Suite negotiation failure',
@@ -111,7 +158,7 @@ ssl_error_codes = {
class Gurl(NSObject):
'''A class for getting content from a URL
- using NSURLConnection and friends'''
+ using NSURLConnection/NSURLSession and friends'''
# since we inherit from NSObject, PyLint issues a few bogus warnings
# pylint: disable=W0232,E1002
@@ -139,8 +186,11 @@ class Gurl(NSObject):
'download_only_if_changed', False)
self.cache_data = options.get('cache_data')
self.connection_timeout = options.get('connection_timeout', 60)
+ if NSURLSESSION_AVAILABLE:
+ self.minimum_tls_protocol = options.get(
+ 'minimum_tls_protocol', kTLSProtocol1)
- self.log = options.get('logging_function', NSLog)
+ self.log = options.get('logging_function', NSLogWrapper)
self.resume = False
self.response = None
@@ -155,6 +205,8 @@ class Gurl(NSObject):
self.expectedLength = -1
self.percentComplete = 0
self.connection = None
+ self.session = None
+ self.task = None
return self
def start(self):
@@ -189,18 +241,34 @@ class Gurl(NSObject):
if 'etag' in stored_data:
request.setValue_forHTTPHeaderField_(
stored_data['etag'], 'if-none-match')
- self.connection = NSURLConnection.alloc().initWithRequest_delegate_(
- request, self)
+ if NSURLSESSION_AVAILABLE:
+ configuration = \
+ NSURLSessionConfiguration.defaultSessionConfiguration()
+ # set minumum supported TLS protocol (defaults to TLS1)
+ configuration.setTLSMinimumSupportedProtocol_(
+ self.minimum_tls_protocol)
+
+ self.session = \
+ NSURLSession.sessionWithConfiguration_delegate_delegateQueue_(
+ configuration, self, None)
+ self.task = self.session.dataTaskWithRequest_(request)
+ self.task.resume()
+ else:
+ self.connection = NSURLConnection.alloc().initWithRequest_delegate_(
+ request, self)
def cancel(self):
'''Cancel the connection'''
if self.connection:
- self.connection.cancel()
+ if NSURLSESSION_AVAILABLE:
+ self.session.invalidateAndCancel()
+ else:
+ self.connection.cancel()
self.done = True
def isDone(self):
'''Check if the connection request is complete. As a side effect,
- allow the delegates to work my letting the run loop run for a bit'''
+ allow the delegates to work by letting the run loop run for a bit'''
if self.done:
return self.done
# let the delegates do their thing
@@ -255,13 +323,8 @@ class Gurl(NSObject):
new_dict[key.lower()] = value
return new_dict
- def connection_didFailWithError_(self, connection, error):
- '''NSURLConnection delegate method
- Sent when a connection fails to load its request successfully.'''
-
- # we don't actually use the connection argument, so
- # pylint: disable=W0613
-
+ def recordError_(self, error):
+ '''Record any error info from completed connection/session'''
self.error = error
# If this was an SSL error, try to extract the SSL error code.
if 'NSUnderlyingError' in error.userInfo():
@@ -270,13 +333,40 @@ class Gurl(NSObject):
if ssl_code:
self.SSLerror = (ssl_code, ssl_error_codes.get(
ssl_code, 'Unknown SSL error'))
+
+ def removeExpectedSizeFromStoredHeaders(self):
+ '''If a successful transfer, clear the expected size so we
+ don\'t attempt to resume the download next time'''
+ if str(self.status).startswith('2'):
+ # remove the expected-size from the stored headers
+ headers = self.get_stored_headers()
+ if 'expected-length' in headers:
+ del headers['expected-length']
+ self.store_headers(headers)
+
+ def URLSession_task_didCompleteWithError_(self, session, task, error):
+ '''NSURLSessionTaskDelegate method.'''
+ # we don't actually use the session or task arguments, so
+ # pylint: disable=W0613
+ if self.destination and self.destination_path:
+ self.destination.close()
+ self.removeExpectedSizeFromStoredHeaders()
+ if error:
+ self.recordError_(error)
+ self.done = True
+
+ def connection_didFailWithError_(self, connection, error):
+ '''NSURLConnectionDelegate method
+ Sent when a connection fails to load its request successfully.'''
+ # we don't actually use the connection argument, so
+ # pylint: disable=W0613
+ self.recordError_(error)
self.done = True
if self.destination and self.destination_path:
self.destination.close()
- # delete it? Might not want to...
def connectionDidFinishLoading_(self, connection):
- '''NSURLConnectionDataDelegat delegate method
+ '''NSURLConnectionDataDelegate method
Sent when a connection has finished loading successfully.'''
# we don't actually use the connection argument, so
@@ -285,18 +375,11 @@ class Gurl(NSObject):
self.done = True
if self.destination and self.destination_path:
self.destination.close()
- if str(self.status).startswith('2'):
- # remove the expected-size from the stored headers
- headers = self.get_stored_headers()
- if 'expected-length' in headers:
- del headers['expected-length']
- self.store_headers(headers)
-
- def connection_didReceiveResponse_(self, connection, response):
- '''NSURLConnectionDataDelegate delegate method
- Sent when the connection has received sufficient data to construct the
- URL response for its request.'''
+ self.removeExpectedSizeFromStoredHeaders()
+ def handleResponse_withCompletionHandler_(
+ self, response, completionHandler):
+ '''Handle the response to the connection'''
self.response = response
self.bytesReceived = 0
self.percentComplete = -1
@@ -330,7 +413,12 @@ class Gurl(NSObject):
# we have a partial for
self.log(
'Can\'t resume download; file on server has changed.')
- connection.cancel()
+ if completionHandler:
+ # tell the session task to cancel
+ completionHandler(NSURLSessionResponseCancel)
+ else:
+ # cancel the connection
+ self.connection.cancel()
self.log('Removing %s' % self.destination_path)
os.unlink(self.destination_path)
# restart and attempt to download the entire file
@@ -355,85 +443,107 @@ class Gurl(NSObject):
# the downloadand for future checking if the file on the server
# has changed
self.store_headers(download_data)
+ if completionHandler:
+ # tell the session task to continue
+ completionHandler(NSURLSessionResponseAllow)
- def connection_willSendRequest_redirectResponse_(
- self, connection, request, response):
+ def URLSession_dataTask_didReceiveResponse_completionHandler_(
+ self, session, task, response, completionHandler):
+ '''NSURLSessionDataDelegate method'''
+ # we don't actually use the session or task arguments, so
+ # pylint: disable=W0613
+ completionHandler.__block_signature__ = objc_method_signature('v@i')
+ self.handleResponse_withCompletionHandler_(response, completionHandler)
+
+ def connection_didReceiveResponse_(self, connection, response):
'''NSURLConnectionDataDelegate delegate method
- Sent when the connection determines that it must change URLs in order to
- continue loading a request.'''
-
+ Sent when the connection has received sufficient data to construct the
+ URL response for its request.'''
# we don't actually use the connection argument, so
# pylint: disable=W0613
+ self.handleResponse_withCompletionHandler_(response, None)
+ def handleRedirect_newRequest_withCompletionHandler_(
+ self, response, request, completionHandler):
+ '''Handle the redirect request'''
if response == None:
- # This isn't a real redirect, this is without talking to a server.
- # Pass it back as-is
- return request
- # But if we're here, it appears to be a real redirect attempt
+ # the request has changed the NSURLRequest in order to standardize
+ # its format, for example, changing a request for
+ # http://www.apple.com to http://www.apple.com/. This occurs because
+ # the standardized, or canonical, version of the request is used for
+ # cache management. Pass the request back as-is
+ # (it appears that at some point Apple also defined a redirect like
+ # http://developer.apple.com to https://developer.apple.com to be
+ # 'merely' a change in the canonical URL.)
+ # Further -- it appears that this delegate method isn't called at
+ # all in this scenario, unlike NSConnectionDelegate method
+ # connection:willSendRequest:redirectResponse:
+ # we'll leave this here anyway in case we're wrong about that
+ if completionHandler:
+ completionHandler(request)
+ return
+ else:
+ return request
+ # If we get here, it appears to be a real redirect attempt
# Annoyingly, we apparently can't get access to the headers from the
# site that told us to redirect. All we know is that we were told
# to redirect and where the new location is.
newURL = request.URL().absoluteString()
self.redirection.append([newURL, dict(response.allHeaderFields())])
newParsedURL = urlparse(newURL)
- # This code was largely based on the work of Andreas Fuchs
+ # This code was largely based on the work of Andreas Fuchs
# (https://github.com/munki/munki/pull/465)
if self.follow_redirects == True or self.follow_redirects == 'all':
# Allow the redirect
self.log('Allowing redirect to: %s' % newURL)
- return request
- elif self.follow_redirects == 'https' and newParsedURL.scheme == 'https':
+ if completionHandler:
+ completionHandler(request)
+ return
+ else:
+ return request
+ elif (self.follow_redirects == 'https'
+ and newParsedURL.scheme == 'https'):
# Once again, allow the redirect
self.log('Allowing redirect to: %s' % newURL)
- return request
+ if completionHandler:
+ completionHandler(request)
+ return
+ else:
+ return request
else:
# If we're down here either the preference was set to 'none',
# the url we're forwarding on to isn't https or follow_redirects
# was explicitly set to False
self.log('Denying redirect to: %s' % newURL)
- return None
+ if completionHandler:
+ completionHandler(None)
+ return
+ else:
+ return None
- def connection_willSendRequestForAuthenticationChallenge_(
- self, connection, challenge):
- '''NSURLConnection delegate method
- Tells the delegate that the connection will send a request for an
- authentication challenge.
- New in 10.7.'''
+ def URLSession_task_willPerformHTTPRedirection_newRequest_completionHandler_(
+ self, session, task, response, request, completionHandler):
+ '''NSURLSessionTaskDelegate method'''
+ # we don't actually use the session or task arguments, so
+ # pylint: disable=W0613
+ self.log(
+ 'URLSession_task_willPerformHTTPRedirection_newRequest_'
+ 'completionHandler_')
+ completionHandler.__block_signature__ = objc_method_signature('v@@')
+ self.handleRedirect_newRequest_withCompletionHandler_(
+ response, request, completionHandler)
+
+ def connection_willSendRequest_redirectResponse_(
+ self, connection, request, response):
+ '''NSURLConnectionDataDelegate method
+ Sent when the connection determines that it must change URLs in order to
+ continue loading a request.'''
# we don't actually use the connection argument, so
# pylint: disable=W0613
-
- self.log('connection_willSendRequestForAuthenticationChallenge_')
- protectionSpace = challenge.protectionSpace()
- host = protectionSpace.host()
- realm = protectionSpace.realm()
- authenticationMethod = protectionSpace.authenticationMethod()
- self.log(
- 'Authentication challenge for Host: %s Realm: %s AuthMethod: %s'
- % (host, realm, authenticationMethod))
- if challenge.previousFailureCount() > 0:
- # we have the wrong credentials. just fail
- self.log('Previous authentication attempt failed.')
- challenge.sender().cancelAuthenticationChallenge_(challenge)
- if self.username and self.password and authenticationMethod in [
- 'NSURLAuthenticationMethodDefault',
- 'NSURLAuthenticationMethodHTTPBasic',
- 'NSURLAuthenticationMethodHTTPDigest']:
- self.log('Will attempt to authenticate.')
- self.log('Username: %s Password: %s'
- % (self.username, ('*' * len(self.password or ''))))
- credential = (
- NSURLCredential.credentialWithUser_password_persistence_(
- self.username, self.password,
- NSURLCredentialPersistenceNone))
- challenge.sender().useCredential_forAuthenticationChallenge_(
- credential, challenge)
- else:
- # fall back to system-provided default behavior
- self.log('Allowing OS to handle authentication request')
- challenge.sender(
- ).performDefaultHandlingForAuthenticationChallenge_(
- challenge)
+ self.log('connection_willSendRequest_redirectResponse_')
+ return self.handleRedirect_newRequest_withCompletionHandler_(
+ response, request, None)
def connection_canAuthenticateAgainstProtectionSpace_(
self, connection, protectionSpace):
@@ -464,17 +574,9 @@ class Gurl(NSObject):
self.log('Allowing OS to handle authentication request')
return False
- def connection_didReceiveAuthenticationChallenge_(
- self, connection, challenge):
- '''NSURLConnection delegate method
- Sent when a connection must authenticate a challenge in order to
- download its request.
- Deprecated in 10.10'''
-
- # we don't actually use the connection argument, so
- # pylint: disable=W0613
-
- self.log('connection_didReceiveAuthenticationChallenge_')
+ def handleChallenge_withCompletionHandler_(
+ self, challenge, completionHandler):
+ '''Handle an authentication challenge'''
protectionSpace = challenge.protectionSpace()
host = protectionSpace.host()
realm = protectionSpace.realm()
@@ -485,7 +587,12 @@ class Gurl(NSObject):
if challenge.previousFailureCount() > 0:
# we have the wrong credentials. just fail
self.log('Previous authentication attempt failed.')
- challenge.sender().cancelAuthenticationChallenge_(challenge)
+ if completionHandler:
+ completionHandler(
+ NSURLSessionAuthChallengeCancelAuthenticationChallenge,
+ None)
+ else:
+ challenge.sender().cancelAuthenticationChallenge_(challenge)
if self.username and self.password and authenticationMethod in [
'NSURLAuthenticationMethodDefault',
'NSURLAuthenticationMethodHTTPBasic',
@@ -497,22 +604,65 @@ class Gurl(NSObject):
NSURLCredential.credentialWithUser_password_persistence_(
self.username, self.password,
NSURLCredentialPersistenceNone))
- challenge.sender().useCredential_forAuthenticationChallenge_(
- credential, challenge)
+ if completionHandler:
+ completionHandler(
+ NSURLSessionAuthChallengeUseCredential, credential)
+ else:
+ challenge.sender().useCredential_forAuthenticationChallenge_(
+ credential, challenge)
else:
# fall back to system-provided default behavior
- self.log('Continuing without credential.')
- challenge.sender(
- ).continueWithoutCredentialForAuthenticationChallenge_(
- challenge)
-
- def connection_didReceiveData_(self, connection, data):
- '''NSURLConnectionDataDelegate method
- Sent as a connection loads data incrementally'''
+ self.log('Allowing OS to handle authentication request')
+ if completionHandler:
+ completionHandler(
+ NSURLSessionAuthChallengePerformDefaultHandling, None)
+ else:
+ if (challenge.sender().respondsToSelector_(
+ 'performDefaultHandlingForAuthenticationChallenge:')):
+ self.log('Allowing OS to handle authentication request')
+ challenge.sender(
+ ).performDefaultHandlingForAuthenticationChallenge_(
+ challenge)
+ else:
+ # Mac OS X 10.6 doesn't support
+ # performDefaultHandlingForAuthenticationChallenge:
+ self.log('Continuing without credential.')
+ challenge.sender(
+ ).continueWithoutCredentialForAuthenticationChallenge_(
+ challenge)
+ def connection_willSendRequestForAuthenticationChallenge_(
+ self, connection, challenge):
+ '''NSURLConnection delegate method
+ Tells the delegate that the connection will send a request for an
+ authentication challenge. New in 10.7.'''
# we don't actually use the connection argument, so
# pylint: disable=W0613
+ self.log('connection_willSendRequestForAuthenticationChallenge_')
+ self.handleChallenge_withCompletionHandler_(challenge, None)
+ def URLSession_task_didReceiveChallenge_completionHandler_(
+ self, session, task, challenge, completionHandler):
+ '''NSURLSessionTaskDelegate method'''
+ # we don't actually use the session or task arguments, so
+ # pylint: disable=W0613
+ completionHandler.__block_signature__ = objc_method_signature('v@i@')
+ self.log('URLSession_task_didReceiveChallenge_completionHandler_')
+ self.handleChallenge_withCompletionHandler_(
+ challenge, completionHandler)
+
+ def connection_didReceiveAuthenticationChallenge_(
+ self, connection, challenge):
+ '''NSURLConnection delegate method
+ Sent when a connection must authenticate a challenge in order to
+ download its request. Deprecated in 10.10'''
+ # we don't actually use the connection argument, so
+ # pylint: disable=W0613
+ self.log('connection_didReceiveAuthenticationChallenge_')
+ self.handleChallenge_withCompletionHandler_(challenge, None)
+
+ def handleReceivedData_(self, data):
+ '''Handle received data'''
if self.destination:
self.destination.write(str(data))
else:
@@ -521,3 +671,16 @@ class Gurl(NSObject):
if self.expectedLength != NSURLResponseUnknownLength:
self.percentComplete = int(
float(self.bytesReceived)/float(self.expectedLength) * 100.0)
+
+ def URLSession_dataTask_didReceiveData_(self, session, task, data):
+ '''NSURLSessionDataDelegate method'''
+ # we don't actually use the session or task arguments, so
+ # pylint: disable=W0613
+ self.handleReceivedData_(data)
+
+ def connection_didReceiveData_(self, connection, data):
+ '''NSURLConnectionDataDelegate method
+ Sent as a connection loads data incrementally'''
+ # we don't actually use the connection argument, so
+ # pylint: disable=W0613
+ self.handleReceivedData_(data)
diff --git a/code/client/munkilib/iconutils.py b/code/client/munkilib/iconutils.py
index 4a56c90d..3a98bf30 100644
--- a/code/client/munkilib/iconutils.py
+++ b/code/client/munkilib/iconutils.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2010-2014 Greg Neagle.
+# Copyright 2010-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/installer.py b/code/client/munkilib/installer.py
index 70553563..edb64cef 100644
--- a/code/client/munkilib/installer.py
+++ b/code/client/munkilib/installer.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -467,7 +467,7 @@ def copyItemsFromMountpoint(mountpoint, itemlist):
# all tests passed, OK to copy
munkicommon.display_status_minor(
"Copying %s to %s" % (source_itemname, full_destpath))
- retcode = subprocess.call(["/bin/cp", "-pR",
+ retcode = subprocess.call(["/usr/bin/ditto", "--noqtn",
source_itempath, full_destpath])
if retcode:
munkicommon.display_error(
@@ -506,20 +506,6 @@ def copyItemsFromMountpoint(mountpoint, itemlist):
"Error setting mode for %s" % (full_destpath))
return retcode
- # remove com.apple.quarantine attribute from copied item
- cmd = ["/usr/bin/xattr", full_destpath]
- proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- (out, dummy_err) = proc.communicate()
- if out:
- xattrs = str(out).splitlines()
- if "com.apple.quarantine" in xattrs:
- dummy_result = subprocess.call(
- ["/usr/bin/xattr", "-d", "com.apple.quarantine",
- full_destpath])
-
# all items copied successfully!
return 0
diff --git a/code/client/munkilib/keychain.py b/code/client/munkilib/keychain.py
index 7f5d9563..29c60913 100644
--- a/code/client/munkilib/keychain.py
+++ b/code/client/munkilib/keychain.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2014 Greg Neagle.
+# Copyright 2014-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/launchd.py b/code/client/munkilib/launchd.py
index 29c45ea2..98aaaa54 100644
--- a/code/client/munkilib/launchd.py
+++ b/code/client/munkilib/launchd.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2011-2014 Greg Neagle.
+# Copyright 2011-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/munkicommon.py b/code/client/munkilib/munkicommon.py
index b55a9dd7..851c73f0 100755
--- a/code/client/munkilib/munkicommon.py
+++ b/code/client/munkilib/munkicommon.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/munkistatus.py b/code/client/munkilib/munkistatus.py
index e459d428..b26147e7 100644
--- a/code/client/munkilib/munkistatus.py
+++ b/code/client/munkilib/munkistatus.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/profiles.py b/code/client/munkilib/profiles.py
index 03d62e8a..606db2ef 100644
--- a/code/client/munkilib/profiles.py
+++ b/code/client/munkilib/profiles.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2014 Greg Neagle.
+# Copyright 2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/code/client/munkilib/removepackages.py b/code/client/munkilib/removepackages.py
index 20e50684..0732217a 100755
--- a/code/client/munkilib/removepackages.py
+++ b/code/client/munkilib/removepackages.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -335,7 +335,7 @@ def ImportBom(bompath, curs):
package database into our internal package database.
"""
# If we completely trusted the accuracy of Apple's database, we wouldn't
- # need the bom files, but in my enviroment at least, the bom files are
+ # need the bom files, but in my environment at least, the bom files are
# a better indicator of what flat packages have actually been installed
# on the current machine.
# We still need to consult Apple's package database
diff --git a/code/client/munkilib/updatecheck.py b/code/client/munkilib/updatecheck.py
index 08094ae2..52209fd7 100755
--- a/code/client/munkilib/updatecheck.py
+++ b/code/client/munkilib/updatecheck.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2009-2014 Greg Neagle.
+# Copyright 2009-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
@@ -1467,7 +1467,7 @@ def lookForUpdates(itemname, cataloglist):
update_list = []
for catalogname in cataloglist:
if not catalogname in CATALOG.keys():
- # in case the list refers to a non-existant catalog
+ # in case the list refers to a non-existent catalog
continue
updaters = CATALOG[catalogname]['updaters']
@@ -1925,7 +1925,7 @@ def processInstall(manifestitem, cataloglist, installinfo):
iteminfo[key] = item_pl[key]
if not 'apple_item' in iteminfo:
- # admin did not explictly mark this item; let's determine if
+ # admin did not explicitly mark this item; let's determine if
# it's from Apple
if isAppleItem(item_pl):
munkicommon.log("Marking %s as apple_item - this may affect \
@@ -2370,7 +2370,7 @@ def processRemoval(manifestitem, cataloglist, installinfo):
iteminfo[key] = uninstall_item[key]
if not 'apple_item' in iteminfo:
- # admin did not explictly mark this item; let's determine if
+ # admin did not explicitly mark this item; let's determine if
# it's from Apple
if isAppleItem(item_pl):
iteminfo['apple_item'] = True
@@ -2892,7 +2892,7 @@ def download_icons(item_list):
def download_client_resources():
"""Download client customization resources (if any)."""
- # Munki's preferences can specify an explict name
+ # Munki's preferences can specify an explicit name
# under ClientResourcesFilename
# if that doesn't exist, use the primary manifest name as the
# filename. If that fails, try site_default.zip
diff --git a/code/client/munkilib/utils.py b/code/client/munkilib/utils.py
index 8fa8b584..f2c0057b 100644
--- a/code/client/munkilib/utils.py
+++ b/code/client/munkilib/utils.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2010-2013 Google Inc. All Rights Reserved.
+# Copyright 2010-2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ Created by Justin McWilliams on 2010-10-26.
Common utility functions used throughout Munki.
-Note: this module should be 100% free of ObjC-dependant Python imports.
+Note: this module should be 100% free of ObjC-dependent Python imports.
"""
diff --git a/code/client/munkilib/version.plist b/code/client/munkilib/version.plist
index f5d1ad37..25962a1c 100644
--- a/code/client/munkilib/version.plist
+++ b/code/client/munkilib/version.plist
@@ -3,6 +3,6 @@
CFBundleShortVersionString
- 2.5.1
+ 2.6.0
diff --git a/code/client/ptyexec b/code/client/ptyexec
index 48cd9907..098965f3 100755
--- a/code/client/ptyexec
+++ b/code/client/ptyexec
@@ -1,7 +1,7 @@
#!/usr/bin/python
# encoding: utf-8
#
-# Copyright 2011-2013 Google Inc. All Rights Reserved.
+# Copyright 2011-2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
diff --git a/code/tools/app2pkg.py b/code/tools/app2pkg.py
deleted file mode 100755
index 3c6351a8..00000000
--- a/code/tools/app2pkg.py
+++ /dev/null
@@ -1,228 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-#
-# Copyright 2009-2014 Greg Neagle.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-app2pkg.py
-
-Created by Greg Neagle on 2009-09-28.
-"""
-
-import sys
-import os
-import stat
-import plistlib
-import subprocess
-import optparse
-import tempfile
-
-
-# change these to suit yourself
-packagemaker = "/Developer/usr/bin/packagemaker"
-pkgidprefix = "com.myorg.pkg."
-pkgoutputdir = "/Users/Shared/pkgs"
-
-
-def makeDMG(pkgpath):
- print "Making disk image..."
- cmd = ["/usr/bin/hdiutil", "create", "-srcfolder", pkgpath,
- pkgpath + ".dmg"]
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Disk image creation failed."
- return(-1)
-
-
-def main():
- # command-line options
- p = optparse.OptionParser()
- p.add_option('--makedmg', '-d', action='store_true',
- help='Makes a disk image containing the package.')
- p.add_option('--name', '-n',
- help='Specify a name for the package.')
- p.add_option('--version', '-v',
- help='Specify a version number for the package.')
- p.add_option('--displayname',
- help='Specify a display name for the package.')
- p.add_option('--description',
- help='Specify a description for the package.')
- p.add_option('--id',
- help='Specify a package id for the package.')
- # Get our options and our package names
- options, app_paths = p.parse_args()
-
- if not os.path.exists(packagemaker):
- print >>sys.stderr, "packagemaker tool not found at %s." % \
- packagemaker
- exit(-1)
-
- if len(app_paths) == 0:
- print >>sys.stderr, "You must specify a path to an application!"
- exit(-1)
-
- if len(app_paths) > 1:
- print >>sys.stderr, "You may package only one app at a time."
- exit(-1)
-
- app_path = app_paths[0]
-
- if not os.path.exists(app_path):
- print "Nothing exists at %s" % app_path
- exit(-1)
-
- mytmpdir = tempfile.mkdtemp()
- if options.name:
- pkgname = options.name
- else:
- pkgname = os.path.splitext(os.path.basename(app_path))[0]
-
- enclosingpath = os.path.dirname(app_path).lstrip("/") or "Applications"
-
- # make packageroot directory
- packageroot = os.path.join(mytmpdir, pkgname)
- os.mkdir(packageroot)
- application_dir = os.path.join(packageroot, enclosingpath)
- os.makedirs(application_dir)
- copytodir = os.path.join(application_dir, os.path.basename(app_path))
- cmd = ['/usr/bin/ditto', '--noqtn', app_path, copytodir ]
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (output, err) = p.communicate()
- if p.returncode != 0:
- print >>sys.stderr, err
- exit(-1)
- if err:
- print >>sys.stderr, err
- exit(-1)
-
- # fix uid/gid/perms on directories
- for dirpath, dirnames, filenames in os.walk(packageroot):
- srcdir = dirpath[len(packageroot):]
- if srcdir == "": srcdir = "/"
- os.chown(dirpath, os.stat(srcdir).st_uid, os.stat(srcdir).st_gid)
- os.chmod(dirpath, stat.S_IMODE(os.stat(srcdir).st_mode))
-
- pkgid = pkgvers = ""
- if options.id:
- pkgid = options.id
- if options.version:
- pkgvers = options.version
-
- appinfo = os.path.join(app_path, "Contents/Info.plist")
- if os.path.exists(appinfo):
- pl = plistlib.readPlist(appinfo)
- if "CFBundleIdentifier" in pl and pkgid == "":
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and pkgvers == "":
- pkgvers = pl["CFBundleShortVersionString"]
-
- if pkgid == "":
- pkgid = pkgidprefix + pkgname.lower().replace(" ","_")
- if pkgvers == "":
- pkgvers = "1.0.0"
-
- print "Package name: %s" % pkgname
- newdisplayname = raw_input("Display name [%s]: "
- % options.displayname)
- options.displayname = newdisplayname or options.displayname
- newdescription = raw_input("Description [%s]: " % options.description)
- options.description = newdescription or options.description
- newid = raw_input("PackageID [%s]: " % pkgid)
- pkgid = newid or pkgid
- newversion = raw_input("Version [%s]: " % pkgvers)
- pkgvers = newversion or pkgvers
-
- print
- print
- print "Package name: %s" % pkgname
- print "Display name: %s" % options.displayname
- print "Description: %s" % options.description
- print "PackageID: %s" % pkgid
- print "Version: %s" % pkgvers
- print
- answer = raw_input("Build the package? [y/n] ")
- if not answer.lower().startswith("y"):
- exit(0)
-
- # build package
- outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
- cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
- '--version', pkgvers,
- '--no-recommend', '--out', outputname, '--verbose',
- '--filter', '.DS_Store$']
- print cmd
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Package creation failed."
- exit(-1)
- else:
- # remove relocatable stuff
- tokendefinitions = os.path.join(outputname,
- "Contents/Resources/TokenDefinitions.plist")
- if os.path.exists(tokendefinitions):
- os.remove(tokendefinitions)
- infoplist = os.path.join(outputname, "Contents/Info.plist")
- pl = plistlib.readPlist(infoplist)
- if 'IFPkgPathMappings' in pl:
- del pl['IFPkgPathMappings']
- plistlib.writePlist(pl, infoplist)
-
- if options.displayname or options.description:
- languages = ['en.lproj', 'English.lproj']
- for item in languages:
- lprojpath = os.path.join(outputname,
- 'Contents/Resources', item)
- if os.path.exists(lprojpath):
- descriptionplist = os.path.join(lprojpath,
- "Description.plist")
- pl = {}
- pl['IFPkgDescriptionTitle'] = (options.displayname or
- pkgname)
- pl['IFPkgDescriptionDescription'] = (options.description
- or "")
- plistlib.writePlist(pl, descriptionplist)
- break
-
- print "Completed package is at %s" % outputname
- if options.makedmg:
- makeDMG(outputname)
-
- #cleanup temp dir
- retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
-
-
-if __name__ == '__main__':
- main()
diff --git a/code/tools/filelist2pkg.py b/code/tools/filelist2pkg.py
deleted file mode 100755
index b4588540..00000000
--- a/code/tools/filelist2pkg.py
+++ /dev/null
@@ -1,354 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-#
-# Copyright 2009-2014 Greg Neagle.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-filelist2pkg.py
-
-Created by Greg Neagle on 2009-11-24.
-"""
-
-import sys
-import os
-import stat
-import plistlib
-import subprocess
-import optparse
-import tempfile
-
-# change these to suit yourself
-packagemaker = '/Developer/usr/bin/packagemaker'
-pkgidprefix = "com.myorg.pkg."
-pkgoutputdir = "/Users/Shared/pkgs"
-
-
-def nameAndVersion(s):
- """
- Splits a string into the name and version numbers:
- 'TextWrangler2.3b1' becomes ('TextWrangler', '2.3b1')
- 'AdobePhotoshopCS3-11.2.1' becomes ('AdobePhotoshopCS3', '11.2.1')
- 'MicrosoftOffice2008v12.2.1' becomes ('MicrosoftOffice2008', '12.2.1')
- """
- index = 0
- for char in s:
- if char in "0123456789":
- possibleVersion = s[index:]
- if not (" " in possibleVersion or "_" in possibleVersion
- or "-" in possibleVersion or "v" in possibleVersion):
- return (s[0:index].rstrip(" .-_v"), possibleVersion)
- index += 1
- # no version number found, just return original string and empty string
- return (s, '')
-
-
-def makeDMG(pkgpath):
- print "Making disk image..."
- cmd = ["/usr/bin/hdiutil", "create", "-srcfolder",
- pkgpath, pkgpath + ".dmg"]
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Disk image creation failed."
- return(-1)
-
-
-def copyItem(sourceitem, packageroot):
- if not os.path.lexists(sourceitem):
- print >>sys.stderr, "%s does not exist!" % sourceitem
- return
-
- paths = []
- pathitem = sourceitem
- while pathitem != "/":
- pathitem = os.path.dirname(pathitem)
- paths.append(pathitem)
-
- paths.reverse()
- for sourcepath in paths:
- targetpath = os.path.join(packageroot, sourcepath.lstrip('/'))
- if not os.path.exists(targetpath):
- os.mkdir(targetpath)
- os.chown(targetpath, os.stat(sourcepath).st_uid,
- os.stat(sourcepath).st_gid)
- os.chmod(targetpath, stat.S_IMODE(os.stat(sourcepath).st_mode))
-
- targetitem = os.path.join(packageroot, sourceitem.lstrip('/'))
- if os.path.isdir(sourceitem) and not os.path.islink(sourceitem):
- os.mkdir(targetitem)
- os.chown(targetitem, os.stat(sourceitem).st_uid,
- os.stat(sourceitem).st_gid)
- os.chmod(targetitem, stat.S_IMODE(os.stat(sourceitem).st_mode))
- elif os.path.islink(sourceitem):
- # make sure we don't follow the symlink
- err = subprocess.call(['/bin/cp', "-a", sourceitem, targetitem])
- else:
- err = subprocess.call(['/bin/cp', "-p", sourceitem, targetitem])
-
-
-def copyItemsFromList(filelist, packageroot):
- f = open(filelist, 'rb')
- while 1:
- item = f.readline()
- if not item:
- break
- if not (item.startswith('.') or item.startswith("/")):
- continue
- item = item.lstrip('./').rstrip('\n')
- sourceitem = os.path.join("/", item)
- copyItem(sourceitem, packageroot)
- f.close()
-
-
-def main():
- # command-line options
- p = optparse.OptionParser()
- p.add_option('--nomakedmg', action='store_true',
- help='Don\'t make a disk image containing the package.')
- p.add_option('--name', '-n',
- help='Specify a name for the package.')
- p.add_option('--version', '-v',
- help='Specify a version number for the package.')
- p.add_option('--id',
- help='Specify a package id for the package.')
- p.add_option('--displayname',
- help='Specify a display name for the package.')
- p.add_option('--description',
- help='Specify a description for the package.')
- p.add_option('--restart', action='store_true',
- help='Restart is required on install.')
- p.add_option('--logout', action='store_true',
- help='Logout is required to install.')
-
- # Get our options and our package names
- options, filelists = p.parse_args()
-
- if not os.path.exists(packagemaker):
- print >>sys.stderr, \
- "packagemaker tool not found at %s." % packagemaker
- exit(-1)
-
- if len(filelists) == 0:
- print >>sys.stderr, "You must specify a file list!"
- exit(-1)
-
- if len(filelists) > 1:
- print >>sys.stderr, "You may convert only one file list at a time."
- exit(-1)
-
- filelist = filelists[0]
- if not os.path.exists(filelist):
- print "No file list at %s" % filelist
- exit(-1)
-
- mytmpdir = tempfile.mkdtemp()
- if options.name:
- pkgname = options.name
- else:
- pkgname = os.path.splitext(os.path.basename(filelist))[0]
-
- packageroot = os.path.join(mytmpdir, pkgname)
- os.mkdir(packageroot)
- copyItemsFromList(filelist, packageroot)
-
- # some default values
- (name, versionInName) = nameAndVersion(pkgname)
- if options.id:
- pkgid = options.id
- else:
- pkgid = pkgidprefix + name.lower()
- if options.version:
- pkgvers = options.version
- elif versionInName:
- pkgvers = versionInName
- else:
- pkgvers = "1.0.0"
-
- infopath = ""
-
- # look through packageroot dir for Receipts
- pkgrootreceipts = os.path.join(packageroot, "Library/Receipts")
- if os.path.exists(pkgrootreceipts):
- receiptlist = os.listdir(pkgrootreceipts)
- if len(receiptlist) == 1:
- receipt = os.path.join(pkgrootreceipts, receiptlist[0])
- infopath = os.path.join(receipt,"Contents/Info.plist")
- if os.path.exists(infopath):
- print "Using package info from %s" % infopath
- else:
- infopath = ""
- else:
- print >>sys.stderr, \
- ("Found multiple receipts, "
- "so cannot determine pkgid and version.")
-
- if not infopath:
- # look for a single application bundle and get info from that
- appinfo = ""
- for dirpath, dirnames, filenames in os.walk(packageroot):
- if dirpath.endswith('.app'):
- if not appinfo:
- appinfo = os.path.join(dirpath, "Contents/Info.plist")
- if not os.path.exists(appinfo):
- appinfo = ""
- else:
- # crap, found more than one.
- appinfo = ""
- print >>sys.stderr, \
- ("Found multiple application bundles, "
- "so cannot determine pkgid and version.")
- break
-
- if appinfo:
- pl = plistlib.readPlist(appinfo)
- if "CFBundleIdentifier" in pl and not options.id:
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and not options.version:
- pkgvers = pl["CFBundleShortVersionString"]
- print "Using pkgid: %s, version: %s from %s" % (pkgid, pkgvers,
- appinfo)
- else:
- # let's look for any Contents/Info.plist
- infoplist = ""
- for dirpath, dirnames, filenames in os.walk(packageroot):
- if dirpath.endswith("/Contents") and \
- "Info.plist" in filenames:
- if not infoplist:
- infoplist = os.path.join(dirpath, "Info.plist")
- if not os.path.exists(infoplist):
- infoplist = ""
- else:
- # found more than one Info.plist
- infoplist = ""
- break
-
- if infoplist:
- pl = plistlib.readPlist(infoplist)
- if "CFBundleIdentifier" in pl and not options.id:
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and not options.version:
- pkgvers = pl["CFBundleShortVersionString"]
- print "Using pkgid: %s, version: %s from %s" % (pkgid,
- pkgvers,
- infoplist)
-
- print "Package name: %s" % pkgname
- newdisplayname = raw_input("Display name [%s]: "
- % options.displayname)
- options.displayname = newdisplayname or options.displayname
- newdescription = raw_input("Description [%s]: " % options.description)
- options.description = newdescription or options.description
- newid = raw_input("PackageID [%s]: " % pkgid)
- pkgid = newid or pkgid
- newversion = raw_input("Version [%s]: " % pkgvers)
- pkgvers = newversion or pkgvers
-
- print
- print
- print "Package name: %s" % pkgname
- print "Display name: %s" % options.displayname
- print "Description: %s" % options.description
- print "PackageID: %s" % pkgid
- print "Version: %s" % pkgvers
- print
- answer = raw_input("Build the package? [y/n] ")
- if not answer.lower().startswith("y"):
- exit(0)
-
- # build package
- outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
- if os.path.exists(outputname):
- retcode = subprocess.call(["/bin/rm", "-rf", outputname])
-
- if infopath:
- cmd = [packagemaker, '--root', packageroot, '--info', infopath,
- '--no-recommend', '--out', outputname, '--verbose',
- '--filter', 'Library/Receipts',
- '--filter', '.DS_Store$']
- else:
- cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
- '--version', pkgvers, '--out', outputname,
- '--verbose', '--no-recommend',
- '--filter', 'Library/Receipts',
- '--filter', '.DS_Store$']
- print cmd
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Package creation failed."
- exit(-1)
- else:
- # remove relocatable stuff
- tokendefinitions = os.path.join(outputname,
- "Contents/Resources/TokenDefinitions.plist")
- if os.path.exists(tokendefinitions):
- os.remove(tokendefinitions)
- infoplist = os.path.join(outputname, "Contents/Info.plist")
- pl = plistlib.readPlist(infoplist)
- if 'IFPkgPathMappings' in pl:
- del pl['IFPkgPathMappings']
-
- if options.restart:
- # add restart info to plist
- pl['IFPkgFlagRestartAction'] = "RequiredRestart"
- elif options.logout:
- # add logout info to plist
- pl['IFPkgFlagRestartAction'] = "RequiredLogout"
-
- plistlib.writePlist(pl, infoplist)
-
- if options.displayname or options.description:
- languages = ['en.lproj', 'English.lproj']
- for item in languages:
- lprojpath = os.path.join(outputname,
- 'Contents/Resources', item)
- if os.path.exists(lprojpath):
- descriptionplist = os.path.join(lprojpath,
- "Description.plist")
- pl = {}
- pl['IFPkgDescriptionTitle'] = (options.displayname or
- pkgname)
- pl['IFPkgDescriptionDescription'] = (options.description
- or "")
- plistlib.writePlist(pl, descriptionplist)
- break
-
- print "Completed package is at %s" % outputname
- if not options.nomakedmg:
- makeDMG(outputname)
-
- #cleanup temp dir
- retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
-
-
-if __name__ == '__main__':
- main()
diff --git a/code/tools/tar2pkg.py b/code/tools/tar2pkg.py
deleted file mode 100755
index 85bb64a3..00000000
--- a/code/tools/tar2pkg.py
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-#
-# Copyright 2011-2014 Greg Neagle.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-tar2pkg.py
-
-Created by Greg Neagle on 2011-06-16.
-"""
-
-import sys
-import os
-import stat
-import plistlib
-import subprocess
-import optparse
-import tempfile
-
-# change these to suit yourself
-packagemaker = "/Developer/usr/bin/packagemaker"
-pkgidprefix = "com.myorg.pkg."
-pkgoutputdir = "/Users/Shared/pkgs"
-
-
-def nameAndVersion(s):
- """
- Splits a string into the name and version numbers:
- 'TextWrangler2.3b1' becomes ('TextWrangler', '2.3b1')
- 'AdobePhotoshopCS3-11.2.1' becomes ('AdobePhotoshopCS3', '11.2.1')
- 'MicrosoftOffice2008v12.2.1' becomes ('MicrosoftOffice2008', '12.2.1')
- """
- index = 0
- for char in s:
- if char in "0123456789":
- possibleVersion = s[index:]
- if not (" " in possibleVersion or "_" in possibleVersion
- or "-" in possibleVersion or "v" in possibleVersion):
- return (s[0:index].rstrip(" .-_v"), possibleVersion)
- index += 1
- # no version number found, just return original string and empty string
- return (s, '')
-
-
-def makeDMG(pkgpath):
- print "Making disk image..."
- cmd = ["/usr/bin/hdiutil", "create", "-srcfolder",
- pkgpath, pkgpath + ".dmg"]
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Disk image creation failed."
- return(-1)
-
-
-def untarItem(sourceitem, packageroot):
- '''untars item into packageroot'''
- os.chdir(packageroot)
- return subprocess.call(['/usr/bin/tar', '-xf', sourceitem])
-
-
-def main():
- # command-line options
- p = optparse.OptionParser()
- p.add_option('--nomakedmg', action='store_true',
- help='Don\'t make a disk image containing the package.')
- p.add_option('--name', '-n',
- help='Specify a name for the package.')
- p.add_option('--version', '-v',
- help='Specify a version number for the package.')
- p.add_option('--id',
- help='Specify a package id for the package.')
- p.add_option('--displayname',
- help='Specify a display name for the package.')
- p.add_option('--description',
- help='Specify a description for the package.')
- p.add_option('--restart', action='store_true',
- help='Restart is required on install.')
- p.add_option('--logout', action='store_true',
- help='Logout is required to install.')
-
- # Get our options and our package names
- options, tarballs = p.parse_args()
-
- if not os.path.exists(packagemaker):
- print >>sys.stderr, \
- "packagemaker tool not found at %s." % packagemaker
- exit(-1)
-
- if len(tarballs) == 0:
- print >>sys.stderr, "You must specify a tar archive!"
- exit(-1)
-
- if len(tarballs) > 1:
- print >>sys.stderr, "You may convert only one tar archive at a time."
- exit(-1)
-
- tarball = tarballs[0]
- if not os.path.exists(tarball):
- print "No tar archive at %s" % tarball
- exit(-1)
-
- mytmpdir = tempfile.mkdtemp()
- if options.name:
- pkgname = options.name
- else:
- pkgname = os.path.splitext(os.path.basename(tarball))[0]
-
- packageroot = os.path.join(mytmpdir, pkgname)
- os.mkdir(packageroot)
- untarItem(tarball, packageroot)
-
- # some default values
- (name, versionInName) = nameAndVersion(pkgname)
- if options.id:
- pkgid = options.id
- else:
- pkgid = pkgidprefix + name.lower()
- if options.version:
- pkgvers = options.version
- elif versionInName:
- pkgvers = versionInName
- else:
- pkgvers = "1.0.0"
-
- infopath = ""
-
- # look through packageroot dir for Receipts
- pkgrootreceipts = os.path.join(packageroot, "Library/Receipts")
- if os.path.exists(pkgrootreceipts):
- receiptlist = os.listdir(pkgrootreceipts)
- if len(receiptlist) == 1:
- receipt = os.path.join(pkgrootreceipts, receiptlist[0])
- infopath = os.path.join(receipt,"Contents/Info.plist")
- if os.path.exists(infopath):
- print "Using package info from %s" % infopath
- else:
- infopath = ""
- else:
- print >>sys.stderr, \
- ("Found multiple receipts, "
- "so cannot determine pkgid and version.")
-
- if not infopath:
- # look for a single application bundle and get info from that
- appinfo = ""
- for dirpath, dirnames, filenames in os.walk(packageroot):
- if dirpath.endswith('.app'):
- if not appinfo:
- appinfo = os.path.join(dirpath, "Contents/Info.plist")
- if not os.path.exists(appinfo):
- appinfo = ""
- else:
- # crap, found more than one.
- appinfo = ""
- print >>sys.stderr, \
- ("Found multiple application bundles, "
- "so cannot determine pkgid and version.")
- break
-
- if appinfo:
- pl = plistlib.readPlist(appinfo)
- if "CFBundleIdentifier" in pl and not options.id:
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and not options.version:
- pkgvers = pl["CFBundleShortVersionString"]
- print "Using pkgid: %s, version: %s from %s" % (pkgid, pkgvers,
- appinfo)
- else:
- # let's look for any Contents/Info.plist
- infoplist = ""
- for dirpath, dirnames, filenames in os.walk(packageroot):
- if dirpath.endswith("/Contents") and \
- "Info.plist" in filenames:
- if not infoplist:
- infoplist = os.path.join(dirpath, "Info.plist")
- if not os.path.exists(infoplist):
- infoplist = ""
- else:
- # found more than one Info.plist
- infoplist = ""
- break
-
- if infoplist:
- pl = plistlib.readPlist(infoplist)
- if "CFBundleIdentifier" in pl and not options.id:
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and not options.version:
- pkgvers = pl["CFBundleShortVersionString"]
- print "Using pkgid: %s, version: %s from %s" % (pkgid,
- pkgvers,
- infoplist)
-
- print "Package name: %s" % pkgname
- newdisplayname = raw_input("Display name [%s]: "
- % options.displayname)
- options.displayname = newdisplayname or options.displayname
- newdescription = raw_input("Description [%s]: " % options.description)
- options.description = newdescription or options.description
- newid = raw_input("PackageID [%s]: " % pkgid)
- pkgid = newid or pkgid
- newversion = raw_input("Version [%s]: " % pkgvers)
- pkgvers = newversion or pkgvers
-
- print
- print
- print "Package name: %s" % pkgname
- print "Display name: %s" % options.displayname
- print "Description: %s" % options.description
- print "PackageID: %s" % pkgid
- print "Version: %s" % pkgvers
- print
- answer = raw_input("Build the package? [y/n] ")
- if not answer.lower().startswith("y"):
- exit(0)
-
- # build package
- outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
- if os.path.exists(outputname):
- retcode = subprocess.call(["/bin/rm", "-rf", outputname])
-
- if infopath:
- cmd = [packagemaker, '--root', packageroot, '--info', infopath,
- '--no-recommend', '--out', outputname, '--verbose',
- '--filter', 'Library/Receipts',
- '--filter', '.DS_Store$']
- else:
- cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
- '--version', pkgvers, '--out', outputname,
- '--verbose', '--no-recommend',
- '--filter', 'Library/Receipts',
- '--filter', '.DS_Store$']
- print cmd
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Package creation failed."
- exit(-1)
- else:
- # remove relocatable stuff
- tokendefinitions = os.path.join(outputname,
- "Contents/Resources/TokenDefinitions.plist")
- if os.path.exists(tokendefinitions):
- os.remove(tokendefinitions)
- infoplist = os.path.join(outputname, "Contents/Info.plist")
- pl = plistlib.readPlist(infoplist)
- if 'IFPkgPathMappings' in pl:
- del pl['IFPkgPathMappings']
-
- if options.restart:
- # add restart info to plist
- pl['IFPkgFlagRestartAction'] = "RequiredRestart"
- elif options.logout:
- # add logout info to plist
- pl['IFPkgFlagRestartAction'] = "RequiredLogout"
-
- plistlib.writePlist(pl, infoplist)
-
- if options.displayname or options.description:
- languages = ['en.lproj', 'English.lproj']
- for item in languages:
- lprojpath = os.path.join(outputname,
- 'Contents/Resources', item)
- if os.path.exists(lprojpath):
- descriptionplist = os.path.join(lprojpath,
- "Description.plist")
- pl = {}
- pl['IFPkgDescriptionTitle'] = (options.displayname or
- pkgname)
- pl['IFPkgDescriptionDescription'] = (options.description
- or "")
- plistlib.writePlist(pl, descriptionplist)
- break
-
- print "Completed package is at %s" % outputname
- if not options.nomakedmg:
- makeDMG(outputname)
-
- #cleanup temp dir
- retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
-
-
-if __name__ == '__main__':
- main()
diff --git a/code/tools/transcript2pkg.py b/code/tools/transcript2pkg.py
deleted file mode 100755
index af1a4907..00000000
--- a/code/tools/transcript2pkg.py
+++ /dev/null
@@ -1,331 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-#
-# Copyright 2009-2014 Greg Neagle.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-transcript2pkg.py
-
-Created by Greg Neagle on 2009-06-04.
-"""
-
-import sys
-import os
-import stat
-import plistlib
-import subprocess
-import optparse
-import tempfile
-
-# change these to suit yourself
-packagemaker = "/Developer/usr/bin/packagemaker"
-t2pkg = "/Users/Shared/bin/t2pkg"
-pkgidprefix = "com.myorg.pkg."
-pkgoutputdir = "/Users/Shared/pkgs"
-
-
-def nameAndVersion(s):
- """
- Splits a string into the name and version numbers:
- 'TextWrangler2.3b1' becomes ('TextWrangler', '2.3b1')
- 'AdobePhotoshopCS3-11.2.1' becomes ('AdobePhotoshopCS3', '11.2.1')
- 'MicrosoftOffice2008v12.2.1' becomes ('MicrosoftOffice2008', '12.2.1')
- """
- index = 0
- for char in s:
- if char in "0123456789":
- possibleVersion = s[index:]
- if not (" " in possibleVersion or "_" in possibleVersion
- or "-" in possibleVersion or "v" in possibleVersion):
- return (s[0:index].rstrip(" .-_v"), possibleVersion)
- index += 1
- # no version number found, just return original string and empty string
- return (s, '')
-
-
-def makeDMG(pkgpath):
- print "Making disk image..."
- cmd = ["/usr/bin/hdiutil", "create", "-srcfolder",
- pkgpath, pkgpath + ".dmg"]
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Disk image creation failed."
- return(-1)
-
-
-def main():
- # command-line options
- p = optparse.OptionParser()
- p.add_option('--makedmg', '-d', action='store_true',
- help='Makes a disk image containing the package.')
- p.add_option('--name', '-n',
- help='Specify a name for the package.')
- p.add_option('--version', '-v',
- help='Specify a version number for the package.')
- p.add_option('--id',
- help='Specify a package id for the package.')
- p.add_option('--displayname',
- help='Specify a display name for the package.')
- p.add_option('--description',
- help='Specify a description for the package.')
- p.add_option('--restart', action='store_true',
- help='Restart is required on install.')
- p.add_option('--logout', action='store_true',
- help='Logout is required to install.')
-
- # Get our options and our package names
- options, transcripts = p.parse_args()
-
- if not os.path.exists(packagemaker):
- print >>sys.stderr, "packagemaker tool not found at %s." % \
- packagemaker
- exit(-1)
- if not os.path.exists(t2pkg):
- print >>sys.stderr, "t2pkg tool not found at %s." % t2pkg
- exit(-1)
-
- if len(transcripts) == 0:
- print >>sys.stderr, "You must specify a transcript!"
- exit(-1)
-
- if len(transcripts) > 1:
- print >>sys.stderr, "You may convert only one transcript at a time."
- exit(-1)
-
- transcript = transcripts[0]
- if transcript.find("/") == -1:
- # transcript name only; we'll prepend the standard
- # client path
- transcript = os.path.join("/var/radmind/client", transcript)
-
- if not os.path.exists(transcript):
- print "No transcript file at %s" % transcript
- exit(-1)
-
- mytmpdir = tempfile.mkdtemp()
- if options.name:
- pkgname = options.name
- else:
- pkgname = os.path.splitext(os.path.basename(transcript))[0]
-
- # make packageroot directory using t2pkg
- packageroot = os.path.join(mytmpdir, pkgname)
- cmd = [t2pkg, '-d', packageroot, '-r', '/', transcript]
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (output, err) = p.communicate()
- if p.returncode != 0:
- print >>sys.stderr, err
- exit(-1)
- if err:
- print >>sys.stderr, err
- exit(-1)
-
- # fix uid/gid/perms on directories
- for dirpath, dirnames, filenames in os.walk(packageroot):
- srcdir = dirpath[len(packageroot):]
- if srcdir == "": srcdir = "/"
- os.chown(dirpath, os.stat(srcdir).st_uid, os.stat(srcdir).st_gid)
- os.chmod(dirpath, stat.S_IMODE(os.stat(srcdir).st_mode))
-
- # some default values
- (name, versionInName) = nameAndVersion(pkgname)
- if options.id:
- pkgid = options.id
- else:
- pkgid = pkgidprefix + name.lower()
- if options.version:
- pkgvers = options.version
- elif versionInName:
- pkgvers = versionInName
- else:
- pkgvers = "1.0.0"
-
- infopath = ""
-
- # look through packageroot dir for Receipts
- pkgrootreceipts = os.path.join(packageroot, "Library/Receipts")
- if os.path.exists(pkgrootreceipts):
- receiptlist = os.listdir(pkgrootreceipts)
- if len(receiptlist) == 1:
- receipt = os.path.join(pkgrootreceipts, receiptlist[0])
- infopath = os.path.join(receipt,"Contents/Info.plist")
- if os.path.exists(infopath):
- print "Using package info from %s" % infopath
- else:
- infopath = ""
- else:
- print >>sys.stderr, \
- "Found multiple receipts, so cannot determine pkgid and version."
-
- if not infopath:
- # look for a single application bundle and get info from that
- appinfo = ""
- for dirpath, dirnames, filenames in os.walk(packageroot):
- if dirpath.endswith('.app'):
- if not appinfo:
- appinfo = os.path.join(dirpath, "Contents/Info.plist")
- if not os.path.exists(appinfo):
- appinfo = ""
- else:
- # crap, found more than one.
- appinfo = ""
- print >>sys.stderr, \
- "Found multiple application bundles, so cannot determine pkgid and version."
- break
-
- if appinfo:
- pl = plistlib.readPlist(appinfo)
- if "CFBundleIdentifier" in pl and not options.id:
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and not options.version:
- pkgvers = pl["CFBundleShortVersionString"]
- print "Using pkgid: %s, version: %s from %s" % (pkgid, pkgvers,
- appinfo)
- else:
- # let's look for any Contents/Info.plist
- infoplist = ""
- for dirpath, dirnames, filenames in os.walk(packageroot):
- if dirpath.endswith("/Contents") and \
- "Info.plist" in filenames:
- if not infoplist:
- infoplist = os.path.join(dirpath, "Info.plist")
- if not os.path.exists(infoplist):
- infoplist = ""
- else:
- # found more than one Info.plist
- infoplist = ""
- break
-
- if infoplist:
- pl = plistlib.readPlist(infoplist)
- if "CFBundleIdentifier" in pl and not options.id:
- pkgid = pl["CFBundleIdentifier"] + ".pkg"
- if "CFBundleShortVersionString" in pl and not options.version:
- pkgvers = pl["CFBundleShortVersionString"]
- print "Using pkgid: %s, version: %s from %s" % (pkgid,
- pkgvers, infoplist)
-
- print "Package name: %s" % pkgname
- newdisplayname = raw_input("Display name [%s]: "
- % options.displayname)
- options.displayname = newdisplayname or options.displayname
- newdescription = raw_input("Description [%s]: " % options.description)
- options.description = newdescription or options.description
- newid = raw_input("PackageID [%s]: " % pkgid)
- pkgid = newid or pkgid
- newversion = raw_input("Version [%s]: " % pkgvers)
- pkgvers = newversion or pkgvers
-
- print
- print
- print "Package name: %s" % pkgname
- print "Display name: %s" % options.displayname
- print "Description: %s" % options.description
- print "PackageID: %s" % pkgid
- print "Version: %s" % pkgvers
- print
- answer = raw_input("Build the package? [y/n] ")
- if not answer.lower().startswith("y"):
- exit(0)
-
- # build package
- outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
- if os.path.exists(outputname):
- retcode = subprocess.call(["/bin/rm", "-rf", outputname])
-
- if infopath:
- cmd = [packagemaker, '--root', packageroot, '--info', infopath,
- '--no-recommend', '--out', outputname, '--verbose',
- '--filter', 'Library/Receipts',
- '--filter', '.DS_Store$',
- '--filter', 'Library/FA/Applications']
- else:
- cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
- '--version', pkgvers, '--out', outputname,
- '--verbose', '--no-recommend',
- '--filter', 'Library/Receipts',
- '--filter', '.DS_Store$',
- '--filter', 'Library/FA/Applications']
- print cmd
- p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
- while True:
- output = p.stdout.readline()
- if not output and (p.poll() != None):
- break
- print output.rstrip("\n")
- sys.stdout.flush()
-
- retcode = p.poll()
- if retcode:
- print >>sys.stderr, "Package creation failed."
- exit(-1)
- else:
- # remove relocatable stuff
- tokendefinitions = os.path.join(outputname,
- "Contents/Resources/TokenDefinitions.plist")
- if os.path.exists(tokendefinitions):
- os.remove(tokendefinitions)
- infoplist = os.path.join(outputname, "Contents/Info.plist")
- pl = plistlib.readPlist(infoplist)
- if 'IFPkgPathMappings' in pl:
- del pl['IFPkgPathMappings']
-
- if options.restart:
- # add restart info to plist
- pl['IFPkgFlagRestartAction'] = "RequiredRestart"
- elif options.logout:
- # add logout info to plist
- pl['IFPkgFlagRestartAction'] = "RequiredLogout"
-
- plistlib.writePlist(pl, infoplist)
-
- if options.displayname or options.description:
- languages = ['en.lproj', 'English.lproj']
- for item in languages:
- lprojpath = os.path.join(outputname,
- 'Contents/Resources', item)
- if os.path.exists(lprojpath):
- descriptionplist = os.path.join(lprojpath,
- "Description.plist")
- pl = {}
- pl['IFPkgDescriptionTitle'] = (options.displayname or
- pkgname)
- pl['IFPkgDescriptionDescription'] = (options.description
- or "")
- plistlib.writePlist(pl, descriptionplist)
- break
-
- print "Completed package is at %s" % outputname
- if options.makedmg:
- makeDMG(outputname)
-
- #cleanup temp dir
- retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
-
-
-if __name__ == '__main__':
- main()
diff --git a/tests/code/client/munkilib/munkicommon_unicode_test.py b/tests/code/client/munkilib/munkicommon_unicode_test.py
index a10442b0..c020435b 100755
--- a/tests/code/client/munkilib/munkicommon_unicode_test.py
+++ b/tests/code/client/munkilib/munkicommon_unicode_test.py
@@ -6,7 +6,7 @@ munkicommon_display_unicode_test.py
Unit tests for munkicommon's display_* functions.
"""
-# Copyright 2014 Greg Neagle.
+# Copyright 2014-2016 Greg Neagle.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
|