2020年9月30日 星期三

[研究] HTTPS 支援加密演算法最大長度金鑰測試

[研究] HTTPS 支援加密演算法最大長度金鑰測試

2020-09-30

考慮幾個因素

Q1.Web Server 是否支援?(IIS, Apache httpd , Tomcat)

Q2.掃描工具是否支援?

********************************************************************************

nmap 7.80 版是目前最新版

https://nmap.org/

受測環境:Windows Server 2019 Standard + IIS (已經關閉不安全的設定,並且經過 WAF )


Starting Nmap 7.80 ( https://nmap.org )

Host is up (0.00s latency).


PORT    STATE SERVICE
443/tcp open  https
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| ssl-enum-ciphers:
|   TLSv1.1:
|     ciphers:
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 1024) - A
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 1024) - A
|     compressors:
|       NULL
|     cipher preference: server
|     warnings:
|       Key exchange (dh 1024) of lower strength than certificate key
|   TLSv1.2:
|     ciphers:
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 1024) - A
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 1024) - A
|       TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (dh 1024) - A
|       TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (dh 1024) - A
|       TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (dh 1024) - A
|       TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (dh 1024) - A
|     compressors:
|       NULL
|     cipher preference: server
|     warnings:
|       Key exchange (dh 1024) of lower strength than certificate key
|_  least strength: A


Nmap done: 1 IP address (1 host up) scanned in 2.02 seconds



********************************************************************************

SSL Labs

https://www.ssllabs.com/ssltest/analyze.html?d=xxx.xxx.xxx.tw


Protocols
TLS 1.3     No
TLS 1.2     Yes
TLS 1.1     Yes
TLS 1.0     No
SSL 3        No
SSL 2        No


Cipher Suites
# TLS 1.2 (suites in server-preferred order)
TLS_RSA_WITH_AES_256_CBC_SHA (0x35)   WEAK   256
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)   WEAK    128
TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d)   WEAK     256
TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c)   WEAK     128
TLS_RSA_WITH_AES_128_GCM_SHA256 (0x9c)   WEAK    128
TLS_RSA_WITH_AES_256_GCM_SHA384 (0x9d)   WEAK   256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)   ECDH secp192r1 (eq. 1536 bits RSA)   FS   WEAK        128
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)   ECDH secp192r1 (eq. 1536 bits RSA)   FS   WEAK        256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)   ECDH secp192r1 (eq. 1536 bits RSA)   FS  128
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH secp192r1 (eq. 1536 bits RSA)   FS 256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)   DH 1024 bits   FS   WEAK      256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33)   DH 1024 bits   FS   WEAK      128
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x6b)   DH 1024 bits   FS   WEAK        256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x67)   DH 1024 bits   FS   WEAK        128
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)   DH 1024 bits   FS   WEAK      128
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)   DH 1024 bits   FS   WEAK       256
# TLS 1.1 (suites in server-preferred order)
TLS_RSA_WITH_AES_256_CBC_SHA (0x35)   WEAK 256
TLS_RSA_WITH_AES_128_CBC_SHA (0x2f)   WEAK 128
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)   ECDH secp192r1 (eq. 1536 bits RSA)   FS   WEAK 128
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)   ECDH secp192r1 (eq. 1536 bits RSA)   FS   WEAK 256
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)   DH 1024 bits   FS   WEAK 256
TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33)   DH 1024 bits   FS   WEAK 128


Handshake Simulation
Android 4.4.2  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Android 5.0.0  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Android 6.0     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Android 7.0     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Android 8.0     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Android 8.1     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Android 9.0     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
BingPreview Jan 2015     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Chrome 49 / XP SP3        RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Chrome 69 / Win 7  R   RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Chrome 70 / Win 10       RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Chrome 80 / Win 10  R RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Firefox 31.3.0 ESR / Win 7      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Firefox 47 / Win 7  R     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Firefox 49 / XP SP3  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Firefox 62 / Win 7  R     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Firefox 73 / Win 10  R   RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Googlebot Feb 2018       RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
IE 11 / Win 7  R     RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
IE 11 / Win 8.1  R  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
IE 11 / Win Phone 8.1  R       RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
IE 11 / Win Phone 8.1 Update  R  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
IE 11 / Win 10  R   RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Edge 15 / Win 10  R      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Edge 16 / Win 10  R      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Edge 18 / Win 10  R      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Edge 13 / Win Phone 10  R   RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Java 8u161      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Java 11.0.3      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Java 12.0.1      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
OpenSSL 1.0.1l  R  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
OpenSSL 1.0.2s  R RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
OpenSSL 1.1.0k  R RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
OpenSSL 1.1.1c  R RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 6 / iOS 6.0.1  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 7 / iOS 7.1  R       RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 7 / OS X 10.9  R   RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 8 / iOS 8.4  R       RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 8 / OS X 10.10  R RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 9 / iOS 9  R  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 9 / OS X 10.11  R RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 10 / iOS 10  R      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 10 / OS X 10.12  R       RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 12.1.2 / MacOS 10.14.6 Beta  R RSA 2048 (SHA256)       TLS 1.2        TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Safari 12.1.1 / iOS 12.3.1  R  RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
Apple ATS 9 / iOS 9  R   RSA 2048 (SHA256)       TLS 1.2     TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA   ECDH secp256r1  FS
Yahoo Slurp Jan 2015      RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS
YandexBot Jan 2015        RSA 2048 (SHA256)       TLS 1.2     TLS_RSA_WITH_AES_256_CBC_SHA  No FS


********************************************************************************

結果:

(Windows Server 2019 + IIS ) + ( nmap 7.80 或 SSLLabs ) 支援金鑰的最大長度

支援 AES_256

支援 SHA384,沒有 SHA512

支援 rsa 2048,沒有 rsa 4096

(完)

相關 

進階加密標準(英語:Advanced Encryption Standard,縮寫:AES) - 維基百科,自由的百科全書
https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86

SHA家族 - 維基百科,自由的百科全書
https://zh.wikipedia.org/wiki/SHA%E5%AE%B6%E6%97%8F

RSA加密演算法 - 維基百科,自由的百科全書
https://zh.wikipedia.org/wiki/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95

 

[研究] 使用者 'xxx' 的 卸除 失敗。 (Microsoft.SqlServer.Smo)資料庫主體在資料庫中擁有結構描述且無法卸除。

[研究] 使用者 'xxx' 的 卸除 失敗。  (Microsoft.SqlServer.Smo)資料庫主體在資料庫中擁有 結構描述 且無法卸除。

2020-09-30

標題: Microsoft SQL Server Management Studio

------------------------------

使用者 'xxx' 的 卸除 失敗。  (Microsoft.SqlServer.Smo)

如需說明,請按一下: https://go.microsoft.com/fwlink?ProdName=Microsoft+SQL+Server&ProdVer=16.100.41011.9+(SqlManagementObjects-master-APPLOCAL)&EvtSrc=Microsoft.SqlServer.Management.Smo.ExceptionTemplates.FailedOperationExceptionText&EvtID=卸除+User&LinkId=20476

實際上該網頁內容已經不存在了。

------------------------------

其他資訊:

執行 Transact-SQL 陳述式或批次時發生例外狀況。 (Microsoft.SqlServer.ConnectionInfo)

------------------------------

資料庫主體在資料庫中擁有 結構描述 且無法卸除。 (Microsoft SQL Server, 錯誤: 15138)

如需說明,請按一下: http://go.microsoft.com/fwlink?ProdName=Microsoft%20SQL%20Server&ProdVer=15.00.2000&EvtSrc=MSSQLServer&EvtID=15138&LinkId=20476

實際上該網頁內容已經不存在了。

********************************************************************************

問題:使用者帳號刪除失敗





********************************************************************************
解決:【結構描述擁有者】換成和【結構描述名稱】相同的。

db_accessadmin、db_ddladmin 和 db_owner 也相同處理。










解決了。

(完)

[研究]Android 手機釋出空間

[研究]Android 手機釋出空間

2020-09-30

釋出空間 - Android說明

https://support.google.com/android/answer/7431795

********************************************************************************

LINE 清除紀錄

首先,點開手機中的LINE 通訊軟體,按左下角「主頁」,按下右上角的「設定圖示」(六角齒輪),點選「聊天」,有個「記錄」選項,可以刪除非必要資料。 




(下圖) 建議先刪除檔案,不夠再刪除記錄



********************************************************************************

清除快取 Cache







同理,可以清除其他應用程式快取。

********************************************************************************

清除Chrome瀏覽資料

清除瀏覽資料 - Android 裝置 - Google Chrome說明

https://support.google.com/chrome/answer/2392709?co=GENIE.Platform%3DAndroid&hl=zh-Hant







********************************************************************************

清理「訊息」應用程式中的對話

https://support.google.com/messages/answer/7028817?hl=zh-Hant

********************************************************************************

如何從Messenger(Android)刪除所有聊天

不支援,只能一篇篇刪除。

********************************************************************************

(完)

2020年9月29日 星期二

[研究]Ionic.Zip 1.9.1.8 即將淘汰,替代套件 DotNetZip >= 1.9.1.8

[研究]Ionic.Zip 1.9.1.8 即將淘汰,替代套件 DotNetZip >= 1.9.1.8

2020-09-29



Ionic Zip Library v1.9.1.6
DotNetZip

NuGet Gallery | Ionic.Zip 1.9.1.8
最後更新 2017/11/22

********************************************************************************
DotNetZip 

https://archive.codeplex.com/?p=dotnetzip

DotNetZip.zip 下載解壓,其中 sourceCode.zip 為2018-01-08建立。


NuGet Gallery | DotNetZip 1.13.8

https://www.nuget.org/packages/DotNetZip/

https://github.com/haf/DotNetZip.Semverd

最後更新2020-05-22

********************************************************************************

[研究][C#][ASP.NET][WebForm] 使用 Ionic.Zip 匯出、匯出 ODF/.ods 檔案

http://shaurong.blogspot.com/2020/09/caspnetwebform-ioniczip-odfods.html

實際測試,NuGet 把 Ionic.Zip 1.9.1.8移除,安裝 DotNetZip 1.13.8,程式仍可正常使用。


using Ionic.Zip;    
// Ionic.Zip 被 DotNetZip 取代,NuGet 安裝 DotNetZip,但是 using 還是用舊名稱 Ionic.Zip 

(完)

[研究][C#]用OdsReaderWrite匯出(寫入)、匯入(讀取) ODF/.ods 檔案 (使用 Ionic.Zip、DotNetZip)

[研究][C#]用OdsReaderWrite匯出(寫入)、匯入(讀取) ODF/.ods 檔案 (使用 Ionic.Zip、DotNetZip)

2020-09-29
2020-12-15 更新
2021-01-03 更新

ODF release dates:

  • ODF 1.0 May 2005
  • ODF 1.1 February 2007
  • ODF 1.2 September 2012
  • ODF 1.3 January 2020

https://wiki.documentfoundation.org/Comparison_of_ODF_software

How to Read and Write ODF/ODS Files (OpenDocument Spreadsheets)
28 Jul 2011
https://www.codeproject.com/Articles/38425/How-to-Read-and-Write-ODF-ODS-Files-OpenDocument-2
所以 odsreadwrite.zip 可能只支援到 ODF, ODS 1.1 版


Visual Studio 2019 v15.6.0

OdsReaderWriter.cs 和 template.ods 取自上面網站

template.ods 放到 bin 目錄。

NuGet 要安裝 Ionic.Zip 套件。(Ionic.Zip 已經淘汰,請改用DotNetZip)

Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ODSExport.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
        </div>
    </form>
</body>
</html>


Default.aspx.cs
using OdsReadWriteNameSpace;    // 要 using  namespace ,才能存取到 OdsReadWriter 這個 class,不是直接  using 這個 OdsReadWriter Class
using System;
using System.Data;
using System.Web.UI.WebControls;

namespace ODSExport
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            DataSet ds = new DataSet("DS1");
            //DataTable dt = new DataTable();
            DataTable dt = ds.Tables.Add("DS1DT");

            DataColumn fNameColumn = new DataColumn();
            fNameColumn.DataType = System.Type.GetType("System.String");
            fNameColumn.ColumnName = "Fname";
            fNameColumn.DefaultValue = "Fname";
            dt.Columns.Add(fNameColumn);

            DataColumn lNameColumn = new DataColumn();
            lNameColumn.DataType = System.Type.GetType("System.String");
            lNameColumn.ColumnName = "Lname";
            lNameColumn.DefaultValue = "Lname";
            dt.Columns.Add(lNameColumn);

            DataRow dr = dt.NewRow();
            dr["Fname"] = "tt0";
            dr["Lname"] = "tt1";
            dt.Rows.Add(dr);

            DataRow dr2 = dt.NewRow();
            dr2["Fname"] = "aa1";
            dr2["Lname"] = "aa2";
            dt.Rows.Add(dr2);

            OdsReaderWriter odsFile = new OdsReaderWriter();
            odsFile.WriteOdsFile(ds, @"C:\TEMP\1.ods");
        }
    }
}

OdsReaderWriter.cs

using System;
using System.Data;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml;
using Ionic.Zip;

namespace OdsReadWriteNameSpace
{
    internal sealed class OdsReaderWriter
    {
        // Namespaces. We need this to initialize XmlNamespaceManager so that we can search XmlDocument.
        private static string[,] namespaces = new string[,]
        {
            {"table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0"},
            {"office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0"},
            {"style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0"},
            {"text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0"},           
            {"draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"},
            {"fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"},
            {"dc", "http://purl.org/dc/elements/1.1/"},
            {"meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"},
            {"number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"},
            {"presentation", "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"},
            {"svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"},
            {"chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0"},
            {"dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"},
            {"math", "http://www.w3.org/1998/Math/MathML"},
            {"form", "urn:oasis:names:tc:opendocument:xmlns:form:1.0"},
            {"script", "urn:oasis:names:tc:opendocument:xmlns:script:1.0"},
            {"ooo", "http://openoffice.org/2004/office"},
            {"ooow", "http://openoffice.org/2004/writer"},
            {"oooc", "http://openoffice.org/2004/calc"},
            {"dom", "http://www.w3.org/2001/xml-events"},
            {"xforms", "http://www.w3.org/2002/xforms"},
            {"xsd", "http://www.w3.org/2001/XMLSchema"},
            {"xsi", "http://www.w3.org/2001/XMLSchema-instance"},
            {"rpt", "http://openoffice.org/2005/report"},
            {"of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2"},
            {"rdfa", "http://docs.oasis-open.org/opendocument/meta/rdfa#"},
            {"config", "urn:oasis:names:tc:opendocument:xmlns:config:1.0"}
        };

        // Read zip stream (.ods file is zip file).
        private ZipFile GetZipFile(Stream stream)
        {
            return ZipFile.Read(stream);
        }

        // Read zip file (.ods file is zip file).
        private ZipFile GetZipFile(string inputFilePath)
        {
            return ZipFile.Read(inputFilePath);
        }

        private XmlDocument GetContentXmlFile(ZipFile zipFile)
        {
            // Get file(in zip archive) that contains data ("content.xml").
            ZipEntry contentZipEntry = zipFile["content.xml"];

            // Extract that file to MemoryStream.
            Stream contentStream = new MemoryStream();
            contentZipEntry.Extract(contentStream);
            contentStream.Seek(0, SeekOrigin.Begin);

            // Create XmlDocument from MemoryStream (MemoryStream contains content.xml).
            XmlDocument contentXml = new XmlDocument();
            contentXml.Load(contentStream);

            return contentXml;
        }

        private XmlNamespaceManager InitializeXmlNamespaceManager(XmlDocument xmlDocument)
        {
            XmlNamespaceManager nmsManager = new XmlNamespaceManager(xmlDocument.NameTable);

            for (int i = 0; i < namespaces.GetLength(0); i++)
                nmsManager.AddNamespace(namespaces[i, 0], namespaces[i, 1]);

            return nmsManager;
        }

        /// <summary>
        /// Read .ods file and store it in DataSet.
        /// </summary>
        /// <param name="inputFilePath">Path to the .ods file.</param>
        /// <returns>DataSet that represents .ods file.</returns>
        public DataSet ReadOdsFile(string inputFilePath)
        {
            ZipFile odsZipFile = this.GetZipFile(inputFilePath);

            // Get content.xml file
            XmlDocument contentXml = this.GetContentXmlFile(odsZipFile);

            // Initialize XmlNamespaceManager
            XmlNamespaceManager nmsManager = this.InitializeXmlNamespaceManager(contentXml);

            DataSet odsFile = new DataSet(Path.GetFileName(inputFilePath));

            foreach (XmlNode tableNode in this.GetTableNodes(contentXml, nmsManager))
                odsFile.Tables.Add(this.GetSheet(tableNode, nmsManager));

            return odsFile;
        }

        // In ODF sheet is stored in table:table node
        private XmlNodeList GetTableNodes(XmlDocument contentXmlDocument, XmlNamespaceManager nmsManager)
        {
            return contentXmlDocument.SelectNodes("/office:document-content/office:body/office:spreadsheet/table:table", nmsManager);
        }

        private DataTable GetSheet(XmlNode tableNode, XmlNamespaceManager nmsManager)
        {
            DataTable sheet = new DataTable(tableNode.Attributes["table:name"].Value);

            XmlNodeList rowNodes = tableNode.SelectNodes("table:table-row", nmsManager);

            int rowIndex = 0;
            foreach (XmlNode rowNode in rowNodes)
                this.GetRow(rowNode, sheet, nmsManager, ref rowIndex);

            return sheet;
        }

        private void GetRow(XmlNode rowNode, DataTable sheet, XmlNamespaceManager nmsManager, ref int rowIndex)
        {
            XmlAttribute rowsRepeated = rowNode.Attributes["table:number-rows-repeated"];
            if (rowsRepeated == null || Convert.ToInt32(rowsRepeated.Value, CultureInfo.InvariantCulture) == 1)
            {
                while (sheet.Rows.Count < rowIndex)
                    sheet.Rows.Add(sheet.NewRow());

                DataRow row = sheet.NewRow();

                XmlNodeList cellNodes = rowNode.SelectNodes("table:table-cell", nmsManager);

                int cellIndex = 0;
                foreach (XmlNode cellNode in cellNodes)
                    this.GetCell(cellNode, row, nmsManager, ref cellIndex);

                sheet.Rows.Add(row);

                rowIndex++;
            }
            else
            {
                rowIndex += Convert.ToInt32(rowsRepeated.Value, CultureInfo.InvariantCulture);
            }

            // sheet must have at least one cell
            if (sheet.Rows.Count == 0)
            {
                sheet.Rows.Add(sheet.NewRow());
                sheet.Columns.Add();
            }
        }

        private void GetCell(XmlNode cellNode, DataRow row, XmlNamespaceManager nmsManager, ref int cellIndex)
        {
            XmlAttribute cellRepeated = cellNode.Attributes["table:number-columns-repeated"];

            if (cellRepeated == null)
            {
                DataTable sheet = row.Table;

                while (sheet.Columns.Count <= cellIndex)
                    sheet.Columns.Add();

                row[cellIndex] = this.ReadCellValue(cellNode);

                cellIndex++;
            }
            else
            {
                cellIndex += Convert.ToInt32(cellRepeated.Value, CultureInfo.InvariantCulture);
            }
        }

        private string ReadCellValue(XmlNode cell)
        {
            XmlAttribute cellVal = cell.Attributes["office:value"];

            if (cellVal == null)
                return String.IsNullOrEmpty(cell.InnerText) ? null : cell.InnerText;
            else
                return cellVal.Value;
        }

        /// <summary>
        /// Writes DataSet as .ods file.
        /// </summary>
        /// <param name="odsFile">DataSet that represent .ods file.</param>
        /// <param name="outputFilePath">The name of the file to save to.</param>
        public void WriteOdsFile(DataSet odsFile, string outputFilePath)
        {

            //ZipFile templateFile = this.GetZipFile(Assembly.GetExecutingAssembly().GetManifestResourceStream("OdsReadWrite.template.ods"));
            //ZipFile templateFile = this.GetZipFile(File.OpenRead(@"C:\CODE\ODSExport\ODSExport\bin\template.ods"));
            //ZipFile templateFile = this.GetZipFile(File.OpenRead(Server.MapPath("/") + "template.ods");

            ZipFile templateFile = this.GetZipFile(File.OpenRead(HttpContext.Current.Server.MapPath("/template.ods"))); 

            XmlDocument contentXml = this.GetContentXmlFile(templateFile);

            XmlNamespaceManager nmsManager = this.InitializeXmlNamespaceManager(contentXml);

            XmlNode sheetsRootNode = this.GetSheetsRootNodeAndRemoveChildrens(contentXml, nmsManager);

            foreach (DataTable sheet in odsFile.Tables)
                this.SaveSheet(sheet, sheetsRootNode);

            this.SaveContentXml(templateFile, contentXml);

            templateFile.Save(outputFilePath);
        }

        private XmlNode GetSheetsRootNodeAndRemoveChildrens(XmlDocument contentXml, XmlNamespaceManager nmsManager)
        {
            XmlNodeList tableNodes = this.GetTableNodes(contentXml, nmsManager);

            XmlNode sheetsRootNode = tableNodes.Item(0).ParentNode;
            // remove sheets from template file
            foreach (XmlNode tableNode in tableNodes)
                sheetsRootNode.RemoveChild(tableNode);

            return sheetsRootNode;
        }

        private void SaveSheet(DataTable sheet, XmlNode sheetsRootNode)
        {
            XmlDocument ownerDocument = sheetsRootNode.OwnerDocument;

            XmlNode sheetNode = ownerDocument.CreateElement("table:table", this.GetNamespaceUri("table"));

            XmlAttribute sheetName = ownerDocument.CreateAttribute("table:name", this.GetNamespaceUri("table"));
            sheetName.Value = sheet.TableName;
            sheetNode.Attributes.Append(sheetName);

            this.SaveColumnDefinition(sheet, sheetNode, ownerDocument);

            this.SaveRows(sheet, sheetNode, ownerDocument);

            sheetsRootNode.AppendChild(sheetNode);
        }

        private void SaveColumnDefinition(DataTable sheet, XmlNode sheetNode, XmlDocument ownerDocument)
        {
            XmlNode columnDefinition = ownerDocument.CreateElement("table:table-column", this.GetNamespaceUri("table"));

            XmlAttribute columnsCount = ownerDocument.CreateAttribute("table:number-columns-repeated", this.GetNamespaceUri("table"));
            columnsCount.Value = sheet.Columns.Count.ToString(CultureInfo.InvariantCulture);
            columnDefinition.Attributes.Append(columnsCount);

            sheetNode.AppendChild(columnDefinition);
        }

        private void SaveRows(DataTable sheet, XmlNode sheetNode, XmlDocument ownerDocument)
        {
            DataRowCollection rows = sheet.Rows;
            for (int i = 0; i < rows.Count; i++)
            {
                XmlNode rowNode = ownerDocument.CreateElement("table:table-row", this.GetNamespaceUri("table"));

                this.SaveCell(rows[i], rowNode, ownerDocument);

                sheetNode.AppendChild(rowNode);
            }
        }

        private void SaveCell(DataRow row, XmlNode rowNode, XmlDocument ownerDocument)
        {
            object[] cells = row.ItemArray;

            for (int i = 0; i < cells.Length; i++)
            {
                XmlElement cellNode = ownerDocument.CreateElement("table:table-cell", this.GetNamespaceUri("table"));

                if (row[i] != DBNull.Value)
                {
                    // We save values as text (string)
                    XmlAttribute valueType = ownerDocument.CreateAttribute("office:value-type", this.GetNamespaceUri("office"));
                    valueType.Value = "string";
                    cellNode.Attributes.Append(valueType);

                    XmlElement cellValue = ownerDocument.CreateElement("text:p", this.GetNamespaceUri("text"));
                    cellValue.InnerText = row[i].ToString();
                    cellNode.AppendChild(cellValue);
                }

                rowNode.AppendChild(cellNode);
            }
        }

        private void SaveContentXml(ZipFile templateFile, XmlDocument contentXml)
        {
            templateFile.RemoveEntry("content.xml");

            MemoryStream memStream = new MemoryStream();
            contentXml.Save(memStream);
            memStream.Seek(0, SeekOrigin.Begin);

            templateFile.AddEntry("content.xml", memStream);
        }

        private string GetNamespaceUri(string prefix)
        {
            for (int i = 0; i < namespaces.GetLength(0); i++)
            {
                if (namespaces[i, 0] == prefix)
                    return namespaces[i, 1];
            }

            throw new InvalidOperationException("Can't find that namespace URI");
        }
    }
}


********************************************************************************

如果產生的 .ods 檔案無法用 Excel 開啟,而出現:

這個活頁簿無法由 Microsoft Excel 開啟或修復,因其已損毀。



可能是資料包含 .ods 檔案無法允許的字元 (一般是不可見字元),但是該字元在 C# 的 string 變數和 SQL Server 的 nvarchar 欄位是允許使用的。最好 String.Replace( originString, ""); 把特殊字元移除。


********************************************************************************

[研究]Ionic.Zip 1.9.1.8 即將淘汰,替代套件 DotNetZip >= 1.9.1.8

實際測試,NuGet 把 Ionic.Zip 1.9.1.8移除,安裝 DotNetZip 1.13.8,程式仍可正常使用。


using Ionic.Zip;    
// Ionic.Zip 被 DotNetZip 取代,NuGet 安裝 DotNetZip,但是 using 還是用舊名稱 Ionic.Zip 

********************************************************************************


注意,此測試寫入 C:\temp 目錄,如果在 Visual Studio 中執行,因為你是 administrator 或 administrators 群組中人,故有權寫入;如果程式用於 IIS 網站,此目錄必須開放讓 IIS_IUSRS 使用者有寫入權限。

(完)

相關

[研究][ASP.NET]使用 OdsReaderWriter + DotNetZip 1.15.0 (Ionic.Zip) 匯出寫入 .ods

2020年9月23日 星期三

[研究] sshd 安裝架設 (CentOS 8.2)

[研究] sshd 安裝架設 (CentOS 8.2)

2020-09-23

sshd = Secure Shell (SSH) Daemon Service

顯示作業系統版本

$ cat /etc/redhat-release

CentOS Linux release 8.2.2004


********************************************************************************

安裝 ssh daemon service

$ sudo yum -y install openssh-server

$ sudo dnf -y install openssh-server


********************************************************************************

顯示狀態

$ sudo systemctl status sshd

立刻啟動、停用、重新啟動

$ sudo systemctl start sshd

$ sudo systemctl stop sshd

$ sudo systemctl restart sshd




********************************************************************************

設定開啟動是否自動啟用 sshd

$ sudo systemctl enable sshd

$ sudo systemctl disable sshd




********************************************************************************

防火牆開放 port 22

$ sudo yum install firewalld

$ sudo systemctl enable firewalld

$ sudo firewall-cmd --zone=public --permanent --add-service=ssh

$ sudo firewall-cmd --reload


檢查狀態
$ sudo firewall-cmd --state
$ firewall-cmd --get-active-zones
$ sudo firewall-cmd --list-all

********************************************************************************

設定檔案

/etc/ssh/sshd_config 

若修改,要重新啟動 sshd

$ sudo systemctl reload sshd

(完)

相關

firewalld

https://firewalld.org/documentation/man-pages/firewall-cmd.html

https://firewalld.org/documentation/utilities/firewall-cmd.html



2020年9月21日 星期一

[研究] 查詢 Windows Update 的 hotfix 更新的取代和被取代(被更新)狀態

[研究] 查詢 Windows Update 的 hotfix 更新的取代和被取代(被更新)狀態

2020-09-21

Microsoft Update Catalog

https://www.catalog.update.microsoft.com/Home.aspx

Ex: 輸入 "Windows Server 2019"

Ex: 輸入 KB4562562


查特定KB資訊方法,例如:KB4557222

https://support.microsoft.com/help/4557222

Click 圖片可以看 100% 原始尺寸




********************************************************************************
查Windows Update更新過哪些 KB







(完)

[研究][JavaScript] SweetAlert 與 SweetAlert2

[研究][JavaScript] SweetAlert 與 SweetAlert2

2020-09-21

SweetAlert 和 SweetAlert2 是 JavaScript 前端套件。

********************************************************************************

SweetAlert

Tristan Edwards (特里斯坦·愛德華茲)

NuGet 安裝時,你會發現一堆類似的稱呼。
不要選第一個 SweetAlert (版本 v1.0.0),
第一個 SweetAlert 的專案目錄其實是
https://github.com/lipis/bootstrap-sweetalert
它是 SweetAlert for Bootstrap

選第二個 SweetAlert.Base (版本 v1.1.3)才是,注意專案 URL 是

https://t4t5.github.com/sweetalert

https://github.com/t4t5/sweetalert

SweetAlert 最後是 14 Dec 2017 釋出的 v2.1.0 版,但是 NuGet 沒有提供這版。
https://github.com/t4t5/sweetalert/releases

官方網站
https://sweetalert.js.org/

SweetAlert.Base 最新 1.1.3 版,最後更新 2015/10/21,已經快5年沒更新了。 
https://www.nuget.org/packages/SweetAlert.Base/

Announcing SweetAlert 2.0
Tristan Edwards  Sep 7, 2017
https://medium.com/@edwards/announcing-sweetalert-2-0-eebd2f66bac2

Tristan Edwards 提到 2014 年建立 SweetAlert,並提到 SweetAlert2,請大家關注 https://sweetalert.js.org/ 網站。
但是網站尚未提到推出 SweetAlert2,也沒提到 limonte 此人。

********************************************************************************

SweetAlert2

目前最新版 v10.3.0,官方網站
https://sweetalert2.github.io/
作者 limonte (Limon Monte,利蒙·蒙特),和 SweetAlert 作者不同。

官方網站;
https://sweetalert2.github.io/

SweetAlert2 各版本,持續更新中,最新 10.3.0 (2020-09-20) 版
https://github.com/sweetalert2/sweetalert2/releases

sweetalert2 CDN Files
https://www.jsdelivr.com/package/npm/sweetalert2

Become a sponsor to limonte
https://github.com/sponsors/limonte
4 years ago I forked SweetAlert and since then SweetAlert2 became twice more popular than the original plugin:
作者 limonte 說 SweetAlert2 是 fork 作者 Tristan Edwards 的 SweetAlert 專案。

Git - 參與一個專案 (Fork 專案)

https://git-scm.com/book/zh-tw/v2/GitHub-%E5%8F%83%E8%88%87%E4%B8%80%E5%80%8B%E5%B0%88%E6%A1%88

SweetAlert2 沒有 NuGet 安裝的方法。

********************************************************************************

敝人沒有詳細研究比較把 SweetAlert 換成 SweetAlert2 後,是否所有 code 都仍能使用。 

(完)

2020年9月18日 星期五

[研究][ASP.NET][WebForm] RichTextEditor 8.0.1 WYSIWYG HTML Editor 試用

[研究][ASP.NET][WebForm] RichTextEditor 8.0.1 WYSIWYG HTML Editor 試用

2020-09-17

WYSIWYG HTML Editor = 所見即所得 HTML 網頁編輯器

WYSIWYG = What You See Is What You Get 

官方網站
https://richtexteditor.com/

Rich Text Editor 範例
https://richtexteditor.com/demos/default.aspx















NuGet 版沒 License 不能用。

********************************************************************************
偶然發現官方網站版本 1.012 和 NuGet 版本 8.2.0.9 差異很大,可能大改版重新給版號。下載用看看

官方網站






********************************************************************************
自己寫個程式試試


Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="RTETest.Default" %>
<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
	<script type="text/javascript" src="/richtexteditor/rte.js"></script>
	<script type="text/javascript" src='/richtexteditor/plugins/all_plugins.js'></script>
</head>
<body>
    <form id="form1" runat="server">
        <input name="htmlcode" id="inp_htmlcode" type="hidden" />
        <div id="div_editor1" class="richtexteditor" style="width: 960px; margin: 0 auto;"></div>       
        <script>
            var editor1 = new RichTextEditor(document.getElementById("div_editor1"));
            editor1.attachEvent("change", function () {
                document.getElementById("inp_htmlcode").value = editor1.getHTMLCode();
            });
        </script>
    </form>
</body>
</html>


Default.aspx 這樣寫也可以
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="RTETest.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
	<script type="text/javascript" src="/richtexteditor/rte.js"></script>
	<script type="text/javascript" src='/richtexteditor/plugins/all_plugins.js'></script>
</head>
<body>
    <form id="form1" runat="server">
        <div id="div_editor1" class="richtexteditor" style="width: 960px; margin: 0 auto;"></div>--%>
        <asp:TextBox ID="TextBox1" runat="server" TextMode="MultiLine" Rows="5" Columns="80"></asp:TextBox>
        <script>
            var editor1 = new RichTextEditor('TextBox1');
        </script>
    </form>
</body>
</html>


(下圖) 執行結果


(下圖) 定價

https://richtexteditor.com/pricing.aspx


(完)